error-handling-patternslisted
Install: claude install-skill KhaledSaeed18/dotclaude
Error handling has one purpose: when something goes wrong, the right party finds out, with enough context to act, and the system stays in a known state. Every pattern below is judged by that.
## First decision: expected outcome or bug?
- **Expected outcomes** (not found, validation failed, permission denied, rate limited, conflict) are part of the function's contract. Represent them in the type: a result type, a discriminated union, a documented typed error. Callers handle them.
- **Bugs and infrastructure failures** (null where impossible, invariant broken, database down, out of memory) are exceptions. They propagate to a boundary that logs them and fails the operation; code in between does not catch them.
Mixing the two (throwing for "not found", or returning null for "database down") produces both noisy logs and silent failures.
## Represent errors with types
- Base error class per domain (`AppError` with `code`, `httpStatus`, `isOperational`), subclasses for categories (`NotFoundError`, `ValidationError`, `ConflictError`, `UpstreamError`). One place maps code to status and to user message.
- Or a result type (`Result<T, E>` / `Either`) for expected outcomes in functional codebases; then errors are values, exhaustively matched, and never forgotten. Do not use both styles in one module.
- Errors carry structured context: the ids involved, the operation, the upstream status. Not the user's secrets, not whole request bodies.
## Where to catch
Catch at **boundaries**: t