Error Handling & Robustness

handling, propagating, or aborting on an error

Imagine you are leading a hike and someone twists an ankle. You have three honest options. You can deal with it yourself - strap it up and carry on (handle). You can radio it up to base camp because they have the stretcher and the call on whether to evacuate (propagate). Or, if it is a true emergency that makes continuing dangerous for everyone, you stop the whole hike immediately (abort). When a program meets a failure, it faces exactly these same three choices, and good error handling is largely about choosing the right one each time.

Handling means resolving the error right here, so that the caller never even needs to know it happened: a failed read from a cache, so you fetch from the slower source instead; a missing optional config, so you fill in a default; a transient network blip, so you retry and succeed. The caller gets a normal success. Propagating means you cannot or should not decide here, so you pass the failure up to your caller (returning an error code, returning NULL, returning a Result) and let a layer with more context choose - while still cleaning up any resources you acquired on the way out. Aborting means the situation is so broken that continuing is unsafe, so you stop the program: this is the right response to a bug (a violated invariant, an impossible state) where there is nothing valid to recover to, and it is done via an assertion failure, abort(), or a clean fatal-error exit. The art is matching the response to the situation: handle recoverable errors you have enough context to fix; propagate recoverable errors that a higher level must decide; abort on bugs and on unrecoverable corruption.

Why it matters: most error-handling mistakes are really mis-chosen responses. Aborting on a recoverable error makes a brittle program that dies on the first missing file when it should have coped. Trying to handle a bug means writing recovery code for a state you do not understand, which usually just hides corruption and limps onward. Silently swallowing an error - neither handling nor propagating nor aborting, just ignoring it - is the worst of all, because the failure is now invisible and will resurface later, far from its cause. The honest framing: these three are a decision you make consciously for each failure, and 'do nothing' is not a fourth option - it is the absence of a decision, and the bug factory behind most robustness failures.

Three responses to the same fopen() returning NULL: HANDLE - fall back to built-in defaults and carry on. PROPAGATE - return an error code to the caller, who decides. ABORT - if this is a mandatory file the whole program needs, print a fatal error and exit non-zero. Which is right depends entirely on what the file means to the program.

Same failure, three valid responses; the right one depends on context, never on habit.

There is no fourth option called 'ignore it'. Doing nothing is not a response - it is the absence of a decision, and it just defers the failure to somewhere far from its cause. Choose handle, propagate, or abort consciously for every failure.

Also called
the three responses to an errorwhat to do when something fails錯誤的三種回應