error-handling · diff
git:20261008.3f25f14 to git:20261015.f440cbb
12 added, 84 removed. Audit A to A.
---
name: error-handling
- description: Use when designing error-handling strategy for a feature — error classification, throw-vs-return conventions, retry/circuit-breaker/fallback decisions, and error-to-user-message mapping. Triggers on "error handling", "retry strategy", "circuit breaker", "error propagation", "错误处理", "重试策略", "熔断", "降级" — also when user says "这个错误该怎么处理" / "要不要重试". Not for diagnosing a specific bug (use debugging); system-level resilience architecture (use architecture).
+ description: Implement explicit failure semantics, bounded retries and recovery. Triggers on "error handling", "错误处理".
---
+ # Error Handling
+
## When to use
- - Designing how errors propagate across layers for a new feature or service boundary
- - Choosing retry, circuit-breaker, or fallback strategy for calls to external systems
- - Establishing throw-vs-return conventions for a module or codebase
- - Mapping internal errors to user-facing messages at an edge boundary
+ - Implement explicit failure semantics, bounded retries and recovery.
- **Not for:** diagnosing a specific bug or test failure (use `debugging`); system-level resilience architecture — cross-service error strategy, overall failure posture (use `architecture`, specifically `architecture/references/error-resilience.md`); API error response envelope shape (use `api-design`); production logging/metrics instrumentation (use `observability`).
+ **Not for:** debugging for a defect; incident-response for live containment.
## Steps
- For system-level resilience architecture (cross-service error propagation, failure posture design), read [architecture's error-resilience reference](${CLAUDE_PLUGIN_ROOT}/skills/03-design/architecture/references/error-resilience.md) first. This skill is the implementation-level companion — the decisions you make at the keyboard when writing the error-handling code.
-
- ### 1. Classify errors
-
- Every error falls into one of four categories, and the category drives the strategy:
-
- | Category | Examples | Strategy |
- |---|---|---|
- | **Transient** | network timeout, 503, connection reset | retry with backoff (step 3) |
- | **Permanent** | 404, 400, logic failure | fail fast, no retry |
- | **Validation** | bad input, schema mismatch | return 422-style result to caller |
- | **Auth** | expired token, insufficient scope | return 401/403, prompt re-auth |
-
- If you can't classify an error, default to permanent — retrying an unclassified error masks real bugs.
-
- ### 2. Choose throw-vs-return convention
-
- Pick one convention per layer and enforce it (architecture ref §1 has the full rationale):
-
- - **Throw (exceptions)** for invariant violations and infrastructure failures — "this should never happen, the operation cannot continue." Catch at boundaries, not at every call.
- - **Return (Result/error values)** for expected, recoverable failures the caller must branch on — not-found, conflict, validation. A 404 is a result, not an exception.
-
- Follow the language ecosystem's dominant convention: Rust/Go return error values; Python/Java/JS throw. Fighting the ecosystem creates inconsistent code.
-
- ### 3. Design retry strategy (transient errors only)
-
- Retry only transient errors. Retrying permanent errors wastes resources and can amplify failures. See [references/retry-patterns.md](references/retry-patterns.md) for formulas and code.
-
- - **Exponential backoff + jitter:** base × 2^n, capped, with jitter to prevent retry stampedes. Full jitter (random between 0 and the capped value) is the safe default.
- - **Max attempts:** 3 covers most transient blips. More than 5 rarely helps and delays failure.
- - **Idempotency:** retry only idempotent operations (GET, PUT, DELETE with stable IDs). For POST, require an idempotency key so a retry after an ambiguous timeout doesn't double-charge or double-create.
- - **Timeout on every call.** A call without a timeout can hang forever and hold resources.
-
- ### 4. Add circuit breaker when retry is not enough
-
- Retry handles isolated transient failures. A circuit breaker protects a struggling downstream from retry storms during a sustained outage.
-
- - **States:** closed (normal) → open (fail fast, no calls) → half-open (probe one request to test recovery).
- - **Open threshold:** N consecutive failures or a failure-rate threshold (e.g., 50% over a rolling window).
- - **Cooldown:** fixed time or exponential reset. Half-open probes one request; success closes, failure re-opens.
- - **Use when:** a downstream dependency has repeated sustained outages and retries would make things worse. Don't add a circuit breaker for a call that rarely fails.
-
- ### 5. Plan graceful degradation
-
- When a dependency fails, decide what the user gets instead:
-
- - **Critical path** (payment, auth, core data): fail the request — no degradation.
- - **Non-critical path** (recommendations, search, analytics): catch the failure, serve a fallback (cached data, empty state, default) — the core page still renders.
- - **Feature-flag kill switch:** for integration failures that recur, a flag to disable the feature path instantly beats debugging under load.
-
- ### 6. Map errors to user-facing messages (at the edge)
-
- Maintain a single mapping from stable error codes to user-facing messages, at the edge boundary (architecture ref §3):
-
- - **Stable error codes** (`PAYMENT_DECLINED`, `RATE_LIMITED`), not free text. The code is the cross-service contract.
- - **Generic external, specific internal:** never expose SQL fragments, stack traces, or internal hostnames to users. Map to "Could not complete this action" + a correlation ID for support.
- - **Actionable:** tell the user what to do ("Check your payment details and try again").
-
- ### 7. Log with structured context
-
- Log every error with enough context to reproduce the diagnosis without re-running:
+ 1. Identify operations, failure classes, ownership and user consequences.
- - Structured fields: error code, operation, correlation ID, request context.
- - No secrets: redact tokens, passwords, PII before logging (see `debugging` skill's redaction discipline).
- - Log at the boundary where the error is handled, not at every catch site — duplicated logs obscure the real failure path.
+ 2. Choose consistent boundary errors or results, preserve causes and sanitize public output.
- ### 8. Test the strategy
+ 3. Retry only transient failures with budgets and safe idempotency; add timeouts, cancellation or circuits when required.
- Write tests for the behavior the strategy promises: retry fires on transient and not on permanent,
- circuit-breaker transitions (open / half-open / closed) advance as configured, and the fallback
- path returns the degraded result.
+ 4. Exercise real code with controlled failure inputs and verify recovery, telemetry and user feedback.
## Verify
- - [ ] Every error in the new code is classified (transient / permanent / validation / auth) — strategy follows classification
- - [ ] Throw-vs-return convention is consistent within each layer; no exceptions used for expected control flow
- - [ ] Retry only fires on transient, idempotent operations; max attempts and backoff are set
- - [ ] Circuit breaker (if used) has defined thresholds, cooldown, and half-open probe logic
- - [ ] Non-critical dependency failures degrade gracefully (fallback/empty state), not cascade to a full page failure
- - [ ] User-facing errors use stable codes + generic messages; no internal details leak
- - [ ] Errors are logged with structured context and a correlation ID; no secrets in log output
- - [ ] Tests cover: retry on transient, no-retry on permanent, circuit-breaker open/half-open/closed transitions, fallback path
+ - Errors are not swallowed; retries terminate and repeated actions and recovery have evidence.
## References
- - [${CLAUDE_PLUGIN_ROOT}/references/engineering-principles.md](${CLAUDE_PLUGIN_ROOT}/references/engineering-principles.md) — discipline shared by every skill
- - [${CLAUDE_PLUGIN_ROOT}/skills/03-design/architecture/references/error-resilience.md](${CLAUDE_PLUGIN_ROOT}/skills/03-design/architecture/references/error-resilience.md) — system-level resilience architecture (cross-layer error propagation, failure posture); this skill is its implementation-level companion
+ - [engineering-principles.md](../../../references/engineering-principles.md) — shared decision boundaries, scope and verification.
+ - [error-resilience.md](../../../skills/03-design/architecture/references/error-resilience.md) — system-level resilience architecture (cross-layer error propagation, failure posture); this skill is its implementation-level companion
- [references/retry-patterns.md](references/retry-patterns.md) — backoff/jitter formulas, circuit breaker state machine, idempotency-key patterns, retry budgets (with code)