Error-Handling Style
Expected failures are values; bugs are exceptions. Validate at the edges, never swallow an error silently, and never paper over one with a fallback that hides it.
This is the cross-cutting policy that the language-specific guides (Typescript) and the safety concepts (No-Fallbacks Policy, Dual-Audience Errors) point at. Source: compiled from TypeScript Style, 2026-05-31
Expected failures return, bugs throw
Model expected, recoverable outcomes (record not found, validation failed, payment declined) as a Result<T, E> and force the caller to handle both arms. Reserve throw for genuinely exceptional, unrecoverable states (invariant violated, programmer error). Do not use exceptions for ordinary control flow, and do not return null to mean five different failures. Source: TypeScript Style, 2026-05-31
Validate external input at the boundary
Every value crossing a trust boundary, request body, query params, env vars, third-party responses, is parsed with a Zod schema before use (see Zod Validation Style). Inside the trust boundary, data is already typed; do not re-validate trusted internal calls. Source: TypeScript Style, 2026-05-31
Never swallow, never blanket-fallback
- No empty
catch {}. If you catch, you handle, rethrow, or log with context. catch (error: unknown), then narrow witherror instanceof Error. Do not assume the caught type.- No silent fallback that masks a real failure. A
try/catchthat returns a default on any error turns an outage into a wrong answer. Fail closed and surface the error instead (see No-Fallbacks Policy).
Source: No-Fallbacks Policy, 2026-05-31
Errors speak to two audiences
An error object carries a clean user-facing message and structured developer detail (cause, origin, machine-readable code). Map internal errors to safe external messages at the system edge, never leak stack traces or internals to the client (see Dual-Audience Errors). Label console.warn/console.error with origin, [FileName::functionName::L42], for traceability. Source: Dual-Audience Errors; TypeScript Style, 2026-05-31
Shape and flow
- Prefer early returns over deeply nested
try/catch; handle the error case first and keep the happy path unindented. - In React, use Error Boundaries for render-time failures; do not try to
catchwhat a boundary should own. - Let a
Resultpropagate up to the edge (an API route or Server Function), and map it to an HTTP status or UI state there, not in the middle of business logic.
Error Contract
| Failure kind | Default shape | Edge mapping |
|---|---|---|
| Validation failure | Typed parse error with field/path detail | 400 or form field state |
| Missing resource | Domain NotFound result |
404 or empty-state branch |
| Conflict | Domain Conflict result with safe reason |
409 |
| Permission failure | Auth/authz result | 401 or 403 |
| Provider failure | Internal error with provider context | Safe external message plus logged cause |
| Invariant violation | Exception or panic with origin | Error boundary, 500, alert, or failed job |
Proof
Tests should assert both arms of expected failures. UI proof should show the user-facing state. Logs or traces should preserve the developer context. A failure path that cannot be tested or observed is not a style exception; it is an unfinished interface.
Style Rule Contract
This page follows Style Rule Contract: name the default pattern, banned pattern, reason, exception, proof, and codification path clearly enough that the next agent can apply the rule without asking Kevin again. If a local repo has a stricter rule, follow the local rule and update the wiki when the decision becomes durable.
Timeline
- 2026-07-01 | Aligned this page with Style Rule Contract so style guidance names default behavior, banned patterns, exceptions, proof, and codification expectations. Source: User request, 2026-07-01
- 2026-07-01 | Added an error contract and proof rules for expected failures, provider failures, invariant violations, edge mapping, and observability. Source: User request, 2026-07-01
- 2026-05-31 | Page created, compiling the error-handling rules scattered across Typescript, No-Fallbacks Policy, and Dual-Audience Errors into one style guide: Result for expected failures, throw for bugs, validate at boundaries, no silent swallow or blanket fallback, dual-audience messages, early returns. Source: compiled from TypeScript Style + No-Fallbacks Policy + Dual-Audience Errors, 2026-05-31