Code that uses try/catch for conditions the caller could check directly — `try { amounts[i] / 100 } catch { return 0 }` where checking bounds is mechanical.
The agent finds try/catch for conditions the caller could check directly; control flow via exceptions obscures the rule.
Exceptions used for predictable, checkable conditions become an explicit precheck the caller can perform, leaving exceptions for truly exceptional cases.
The precheck appears at the point of decision; the agent reads the code top-to-bottom as the rule, with exceptions reserved for truly exceptional cases.
Before the refactoring
try {return amounts[i] / 100;} catch (e) {return 0;}
After the refactoring
if (i >= amounts.length) return 0;return amounts[i] / 100;
Exception flow obscures the rule; debuggers stop on benign throws; reasoning about the happy path requires reading the catch handler.
The agent debugging exception flow must reason about catch handlers; benign throws fire breakpoints; the rule that 'should fire' isn't obvious from reading.
Race conditions: the precheck may pass and the operation still fail (TOCTOU). Use prechecks only for conditions the caller can verify without a race.
Race conditions: the precheck may pass and the operation still fail (TOCTOU); the agent using prechecks must verify the caller can check the condition atomically.
The error path is local and visible; reading code top-to-bottom describes the rules rather than the failure response; debuggers stop catching benign throws.
The agent reads the rule top-to-bottom; debuggers stop catching benign throws; exception handlers reserve for truly exceptional cases.
Replacing every exception with a precheck — including ones for conditions the caller can't atomically verify (TOCTOU race windows).
Replacing exceptions with prechecks for conditions the caller can't atomically verify introduces race windows the agent's local tests won't catch.