AI-written error handling tends to treat a timeout as a clean no, when it actually means I don't know. That single mix-up is how a retry turns one successful upload into two, and why the code that looks most careful is often the code doing the most damage.
The Details:
Models learn error handling from tutorials, where the pattern is try, catch, log, move on. That pattern satisfies any prompt asking for robustness, but it assumes failure is binary: either the call worked or it threw. Real systems fail in the middle, leaving ambiguous states that a catch block was never built to interpret.
A network timeout after a successful upload gets treated as a full failure, so the code retries. The original write already landed, so now a duplicate flows into billing, reporting, and every downstream system that trusts that record as real. Nothing crashed, so nobody notices until the numbers stop reconciling.
The gap closes with architecture, not more exception handling. An idempotency key, a reconciliation step, or an explicit pending state does what a catch block cannot: it names the ambiguity instead of letting it disappear into a log line. Building that state directly into the data model forces something downstream to resolve it rather than silently trust it.
Three review questions catch most of this risk before it ships: what state is the system in after this catch block runs, what happens if the first attempt already succeeded, and who downstream will treat this fallback value as real. That last question gets skipped most often, because a sensible default feels safer than a crash. It usually is not; a crash stops the damage, a silent default just keeps running in a wrong state.
Bottom Line: A system that fails loudly is easier to trust than one that fails quietly and calls it resilience.
Enjoy this article?
Listen to the Claude Code Conversations radio show or join the community.