Error Handling Architecture
When this skill activates
This skill activates when designing error hierarchies, implementing error boundaries, configuring retry policies, setting up error reporting (Sentry/Datadog/Bugsnag), building dead letter queue processing, or establishing error handling patterns across a codebase. It applies to both frontend and backend error architecture.
Mandatory actions when this skill is active
Before
- Map the system boundaries where errors cross layers (UI → API → service → DB → external).
- Identify the error reporting tool in use or to be adopted (Sentry, Datadog, Bugsnag, custom).
- Categorize existing errors: which are retryable, which are permanent, which are user-facing.
- Determine SLA for error detection and alerting (time from error to notification).
- Review current error handling for anti-patterns (swallowed errors, generic catches, leaked internals).
During
Error Hierarchy Design:
BaseError (abstract)
├── ValidationError — Invalid input (400). User can fix and retry.
├── NotFoundError — Resource doesn't exist (404). Client should not retry.
├── AuthenticationError — Identity unknown (401). Redirect to login.
├── AuthorizationError — Identity known, permission denied (403). Contact admin.
├── ConflictError — State conflict (409). Client should refresh and retry.
├── RateLimitError — Too many requests (429). Client must back off.
├── ExternalServiceError — Upstream dependency failed (502/503). Retry with backoff.
└── InternalError — Unexpected failure (500). Alert engineers.
- Every error class carries:
code(machine-readable),message(human-readable),context(debugging data),retryable(boolean). - NEVER use generic
ErrororExceptionfor domain errors. Always use typed errors. - Error codes follow a namespace:
AUTH_001,PAYMENT_002,INVENTORY_003.
Error Boundaries (Defense in Depth):
- React UI:
ErrorBoundarycomponents at route level and critical widget level. Show user-friendly fallback, report to Sentry. - API Controllers: Catch all errors at the handler level. Transform to appropriate HTTP status and structured response.
- Service Layer: Catch errors from dependencies, add context, re-throw as domain errors.
- Infrastructure: Global uncaught exception handler as last resort (log + alert + graceful shutdown).
- Each boundary: catch, enrich with context, decide (retry/propagate/absorb), report.
Retry Policies:
- Exponential backoff:
delay = baseDelay * 2^attempt(e.g., 100ms, 200ms, 400ms, 800ms). - Jitter: Add random variance to prevent thundering herd (
delay + random(0, delay * 0.1)). - Max retries: 3 for fast operations, 5 for critical operations, 0 for non-idempotent writes.
- Idempotency keys: Required for retrying write operations (prevent double-processing).
- Circuit breaker: After N consecutive failures, stop trying for a cooldown period. Prevents cascade.
- Retry only retryable errors: Network timeouts (yes), validation errors (no), 500s (maybe, with limit).
Dead Letter Queues (Async Error Handling):
- Messages that fail processing after max retries go to the DLQ.
- DLQ messages must retain: original message, error details, attempt count, timestamp of each failure.
- Alerting: notify when DLQ depth exceeds threshold (e.g., >10 messages in 5 minutes).
- Manual replay: tooling to inspect DLQ messages and replay them after fix is deployed.
- Poisoned messages: after replay still fails, move to permanent failure store with investigation ticket.
- Never silently drop messages. Every message must reach success or explicit human acknowledgment.
Error Reporting (Sentry Integration Pattern):
- Breadcrumbs: Log navigation, API calls, user actions leading up to the error.
- Tags: environment, service, user_id, transaction_id, feature_flag.
- Context: request body (sanitized), response status, database query (no PII).
- Grouping: Configure fingerprinting so related errors group together (not 1000 separate issues).
- Alerting: New issues → Slack. Regression (resolved issue re-opens) → PagerDuty.
- Sampling: 100% for errors, 10-20% for transactions/performance in production.
- PII scrubbing: Strip emails, tokens, passwords before sending to error reporting.
Structured Error Responses (API):
{
"error": {
"code": "VALIDATION_001",
"message": "Email address is invalid",
"details": [
{ "field": "email", "constraint": "Must be a valid email format" }
],
"retryable": false,
"request_id": "req_abc123"
}
}
- Always include
request_idfor correlation with server logs. - Never expose stack traces, internal paths, or database details in API responses.
- Use
detailsarray for field-level validation errors. - Include
retryableflag so clients can implement automatic retry logic.
Error Propagation Rules:
- Errors propagate UP (from infrastructure to service to controller to client).
- At each layer: catch, add context specific to that layer, re-throw as appropriate type.
- NEVER swallow errors silently (
catch (e) {}). At minimum: log and re-throw. - Transform errors at boundaries (internal DB error becomes generic 500 to the client).
- Preserve the original error as
causefor debugging (Error Cause chain /causeproperty).
After
- Error hierarchy covers all known failure modes in the system.
- Every boundary has explicit error handling (no unhandled promise rejections, no bare throws).
- Retry policies are configured with backoff, jitter, and max attempts.
- Error reporting captures sufficient context for debugging without exposing PII.
- Alerting is configured for new errors, regressions, and DLQ threshold breaches.
Self-check before task completion
- All errors use typed error classes from the hierarchy (no generic
Errorthrows). - Error boundaries exist at every system layer (UI, API, service, infrastructure).
- Retry policies use exponential backoff with jitter and respect idempotency.
- Dead letter queues are configured for async processing with alerting and replay tooling.
- Error reporting includes breadcrumbs, tags, and context (without PII).
- API error responses are structured with code, message, retryable flag, and request_id.
- No errors are silently swallowed anywhere in the codebase.
- Error messages are actionable for the audience (user-friendly in UI, detailed in logs).