Error handling and resilience
Skill aneja5/forge-skills/skills/error-handling-and-resilience
Use when establishing error handling patterns for a project, when adding retry logic, circuit breakers, or graceful degradation, when reviewing how a service handles failures, or when an incident reveals that a failure mode was silently swallowed.From its SKILL.md
npx -y skills add aneja5/forge-skills --skill error-handling-and-resilienceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.6 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it
Error Handling and Resilience
Overview
Define the project's failure philosophy before failures happen. Output is .forge/error-handling.md — an error-class taxonomy, retry and timeout discipline, circuit breaker policy, graceful degradation paths, user-facing error message rules, and rollback/compensation patterns for irreversible flows. Downstream skills (observability, incident-response-and-postmortems) consume this file.
When to Use
- A new service is being designed and needs an error model before code lands
- An incident revealed a silently-swallowed failure
- External calls (DB, API, queue, LLM) are being added to a hot path
- Code review is flagging inconsistent error handling across modules
- Production logs are full of unclassified errors with no caller action
When NOT to Use
- The codebase already has a documented error taxonomy and the change is local to one module
- One-off scripts or throwaway prototypes
- The work is a config tweak with no failure surface
Common Rationalizations
| Thought | Reality |
|---|---|
| "try-catch is enough" | Bare catch swallows context. Without classification, the same handler runs for transient DB blips and bad user input. |
| "We'll add retries later" | Retries are architectural — adding them after the fact requires every caller to change. |
| "Errors are rare" | Rare errors in production are the ones that page you at 3am. They're rare because they require specific conditions, not because they don't matter. |
| "Just log and continue" | Silent failures compound. Downstream code now sees inconsistent state and no signal that something went wrong. |
| "External call without timeout is fine, the library has defaults" | Library defaults are usually 30s-infinity. Your user is gone in 8 seconds. |
| "Retry forever, eventually it'll work" | Retry storms amplify outages. Without a cap, you DDoS yourself. |
Red Flags
try { ... } catch (e) {}— empty catchconsole.log(err); return— log and continue without classificationwhile (true) { try { ... } catch { } }— unbounded retryif (err.message.includes("not found"))— string-matching errors- Promise without
.catchor async function withouttry fetch(url)with no timeout, no AbortController, no deadline- No distinction between transient (retry) and permanent (escalate) failures
- "Error handling: TBD" anywhere in a contract
Core Process
Step 1: Inventory failure modes per component
For each component (service, module, integration), list every operation that can fail. For each:
- What goes wrong (network, validation, business rule, dependency, race)
- What state is left behind (partial write, dangling lock, orphan record)
- Who notices (user, ops, no one)
Step 2: Classify each failure mode
Three classes:
- Transient — retry with backoff (network blips, rate limits, lock contention, 5xx from a dependency)
- Permanent — do not retry, escalate (bad input, 4xx, business rule violation, deleted resource)
- User-correctable — surface a non-technical message; the user can fix and retry (missing field, expired card, insufficient funds)
Step 3: Assign handling pattern per class
| Class | Pattern |
|---|---|
| Transient | Exponential backoff with jitter, max attempts, deadline, idempotency key |
| Permanent | Wrap with structured error type, return upward, do NOT retry, alert if unexpected |
| User-correctable | Map to user-facing message in catalog, return 4xx, log only at DEBUG |
Step 4: Write the error taxonomy
In .forge/error-handling.md:
- Named error types per class with conditions and HTTP/exit codes
- Retry config (max attempts, base delay, jitter, deadline) per call type
- Circuit breaker thresholds per dependency
- Timeout per external call (NEVER unbounded)
- User-facing message catalog with stable codes
Step 5: Define logging + alerting per class
- Transient at INFO during retries, WARN after max attempts exceeded
- Permanent at ERROR, alert if rate exceeds threshold
- User-correctable at DEBUG (do not page humans for user typos)
- Cross-reference
observabilityskill for correlation IDs
Step 6: Write rollback / compensation for irreversible flows
For any operation that crosses a boundary (payment, email send, external write, queue publish):
- Define the idempotency key
- Define the compensation action if a downstream step fails
- Document the reconciliation job
Step 7: Header
Prepend a forge:meta header to .forge/error-handling.md (generated_by: error-handling-and-resilience, generated_at: <ISO 8601 UTC with Z>, depends_on: [.forge/architecture.md] — paths only, never hashes, generated_from: {.forge/architecture.md: <upstream content_hash AT generation time>}, content_hash: <sha256 first 8 of THIS file's body>). See forge-dependency-graph.
Verification
-
.forge/error-handling.mdwritten - Every external call in the codebase has an explicit timeout
- Every retry has max attempts + backoff + deadline
- Every error type has a class (transient / permanent / user-correctable)
- Every user-facing error has a non-technical message in the catalog
- No bare
catch (e) {}in the codebase (grep verified) - Every irreversible flow has a documented compensation path
- Circuit breaker thresholds defined for every external dependency
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most error diagnosis skills give in ~1.3k tokens
Counted across 135 of the 162 authors here whose files we hold, read 2026-09-06
- Handle, re-throw, or log in every catch blockin 12 of 135, across 7 files
- Use typed error classes over string messagesin 11 of 135, across 6 files
- Log full error context server-sidein 10 of 135, across 5 files
- Document every error code clients may receivein 9 of 135, across 4 files
- Surface errors at the boundary where they occurin 9 of 135, across 4 files
- Wrap React components in an ErrorBoundaryin 9 of 135, across 4 files
- Wrap errors with context, never lose the originalin 9 of 135, across 4 files
- Use the standard error envelope for API responsesin 9 of 135, across 4 files
- Retry only retriable errors, never 4xx client errorsin 8 of 135, across 3 files
- Retry transient failures with exponential backoff and jitterin 8 of 135
- Show users friendly messages without technical detailsin 7 of 135, across 3 files
- Use the Result pattern for expected failuresin 7 of 135, across 5 files
Said here and by no other author read
- Inventory failure modes per component
- Classify each failure as transient, permanent, or user-correctable
- Assign a handling pattern per failure class
- Write the error taxonomy document
- Cap every retry with max attempts, backoff, and deadline
- Define circuit breaker thresholds per dependency
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.