agentleFS
nisarthnisarth/claude-skills-store/skills/engineering/error-handling/SKILL.md

error-handling

Skill1 starsChanged 7 months agoMITView on GitHub

Design error handling strategies: error hierarchies, retry policies, circuit breakers, graceful degradation, and error reporting. Trigger with "error handling", "how should I handle errors", "retry logic", "circuit breaker", "graceful degradation", "error reporting", "error hierarchy", or when the user needs help designing robust failure handling in their application.

80 other projects use a version of this, 1 of them edited.

Adopted by

Most recently taken up by

Used with

Projects with it also use convention-learner, caching and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • codewithmukeshSkill

    convention-learner

    Detects and enforces project-specific coding conventions by analyzing existing codebase patterns. Learns naming conventions, folder structure, test organization, and coding style from the existing code. Load when: "conventions", "coding standards", "project patterns", "enforce style", "detect patterns", "learn conventions", "code consistency".

    Also in 3 projects that use this

  • wpankSkill

    caching

    Caching strategies, invalidation, eviction policies, HTTP caching, distributed caching, and anti-patterns. Use when designing cache layers, choosing eviction policies, debugging stale data, or optimizing read-heavy workloads.

    Also in 6 projects that use this

  • diego-tobalinaSkill

    logging

    Logging best practices for Java (SLF4J/Logback) and Node.js (Winston/Pino). Use for structured logging, log levels, and avoiding sensitive data exposure.

    Also in 12 projects that use this

  • codewithmukeshSkill

    opentelemetry

    OpenTelemetry observability for .NET 10 applications. Covers traces, metrics, and logs using the OpenTelemetry SDK with OTLP export. Includes custom ActivitySource, IMeterFactory metrics, resource configuration, and Aspire Dashboard integration. Load this skill when setting up distributed tracing, custom metrics, OTLP export, or when the user mentions "OpenTelemetry", "OTLP", "traces", "spans", "Activity", "ActivitySource", "metrics", "IMeterFactory", "Meter", "Counter", "Histogram", "Gauge", "telemetry", "observability", "distributed tracing", "OTEL", or "Aspire Dashboard".

    Also in 3 projects that use this

  • MovGP0Skill

    modern-csharp

    Modern C# style guidance for Bendex and MicroSea code changes, including primary constructors, expression-bodied members, var usage, target-typed new, lambdas, and pattern matching.

    Also in 3 projects that use this

  • MicrosoftSkill

    resilience

    Apply resilience patterns (retry, backoff, circuit breaking) when writing or reviewing code that makes outbound HTTP/REST calls, database operations, or any I/O that can experience transient failures. Use this skill when: (1) writing new code that calls external services or APIs, (2) reviewing code that uses fetch, HTTP clients, or service-to-service calls, (3) adding queue consumers or workers that interact with remote systems, (4) debugging intermittent failures in production.

    Also in 4 projects that use this

The file

Show its rules and text

We split this skill into the rules it gives an agent

16 rules. 0 are common, 14 are distinctive, and 2 are very specific to this project.

  • 0

    Common

    Most of the 64 edited versions we read keep these.

  • 14

    Distinctive

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read the codebase: Search for try/catch, catch, .catch(, rescue, except, recover patterns.

    line 35
  2. Distinctive:

    Check for swallowed errors: Look for empty catch blocks or catch blocks that only log.

    line 36
  3. Distinctive:

    Review error responses: Check API error response formats for consistency.

    line 37
  4. Distinctive:

    Check logging: Look for structured logging setup (winston, pino, loguru, slog).

    line 38
  5. Distinctive:

    Check monitoring: Look for error tracking integration (Sentry, Datadog, Bugsnag).

    line 39
  6. Distinctive:

    No retry logic for transient failures (network timeouts, rate limits).

    line 47
  7. Distinctive:

    Create a structured error class hierarchy for the application:

    line 52
  8. Distinctive:

    Full functionality: All services healthy, all features available.

    line 306
  9. Distinctive:

    Read-only mode: Write services down, reads still work from cache or replica.

    line 308
  10. Distinctive:

    Use error codes that the client can map to user-friendly messages.

    line 357
  11. Distinctive:

    Action: Read the project structure, design an error class hierarchy, create a global error handler, add retry logic for external service …

    line 403
  12. Very specific:

    Use typed errors, not string messages, as the basis for error handling decisions.

    line 418
  13. Distinctive:

    Always include context in error logs: request ID, user ID, operation name, and relevant parameters.

    line 419
  14. Distinctive:

    Never log sensitive data (passwords, tokens, PII) in error messages or context.

    line 420
  15. Distinctive:

    Use circuit breakers for external dependencies to prevent cascading failures.

    line 423
  16. Distinctive:

    Test error paths as thoroughly as happy paths.

    line 424

“Common” is measured: kept by at least 6 in 10 of the 64 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Error Handling

Design robust error handling strategies for applications. Covers error classification hierarchies, retry policies with backoff, circuit breaker patterns, graceful degradation, structured error reporting, and user-facing error messages.

Workflow

Step 1: Assess the Current State

Understand how errors are currently handled:

  • Read the codebase: Search for try/catch, catch, .catch(, rescue, except, recover patterns.
  • Check for swallowed errors: Look for empty catch blocks or catch blocks that only log.
  • Review error responses: Check API error response formats for consistency.
  • Check logging: Look for structured logging setup (winston, pino, loguru, slog).
  • Check monitoring: Look for error tracking integration (Sentry, Datadog, Bugsnag).

Common anti-patterns to identify:

  • Catching Exception or Error base class everywhere (too broad).
  • Swallowing errors silently (catch (e) {}).
  • Returning null on failure instead of throwing (hides errors).
  • Inconsistent error response formats across API endpoints.
  • Missing error boundaries in frontend (entire app crashes on one component error).
  • No retry logic for transient failures (network timeouts, rate limits).
  • Logging sensitive data in error messages (passwords, tokens, PII).

Step 2: Design Error Hierarchy

Create a structured error class hierarchy for the application:

TypeScript/JavaScript:

// Base application error
class AppError extends Error {
  constructor(
    message: string,
    public readonly code: string,
    public readonly statusCode: number = 500,
    public readonly isOperational: boolean = true,
    public readonly context?: Record<string, unknown>
  ) {
    super(message);
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }
}

// Specific error types
class ValidationError extends AppError {
  constructor(message: string, public readonly fields: FieldError[]) {
    super(message, 'VALIDATION_ERROR', 422);
  }
}

class NotFoundError extends AppError {
  constructor(resource: string, id: string) {
    super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
  }
}

class AuthenticationError extends AppError {
  constructor(message = 'Authentication required') {
    super(message, 'UNAUTHENTICATED', 401);
  }
}

class AuthorizationError extends AppError {
  constructor(message = 'Insufficient permissions') {
    super(message, 'FORBIDDEN', 403);
  }
}

class ConflictError extends AppError {
  constructor(message: string) {
    super(message, 'CONFLICT', 409);
  }
}

class ExternalServiceError extends AppError {
  constructor(service: string, cause?: Error) {
    super(`External service failure: ${service}`, 'EXTERNAL_SERVICE_ERROR', 502);
    if (cause) this.cause = cause;
  }
}

class RateLimitError extends AppError {
  constructor(public readonly retryAfter: number) {
    super('Rate limit exceeded', 'RATE_LIMITED', 429);
  }
}

Python:

class AppError(Exception):
    def __init__(self, message: str, code: str, status_code: int = 500,
                 is_operational: bool = True, context: dict | None = None):
        super().__init__(message)
        self.code = code
        self.status_code = status_code
        self.is_operational = is_operational
        self.context = context or {}

class ValidationError(AppError):
    def __init__(self, message: str, fields: list[dict]):
        super().__init__(message, "VALIDATION_ERROR", 422)
        self.fields = fields

class NotFoundError(AppError):
    def __init__(self, resource: str, id: str):
        super().__init__(f"{resource} with id {id} not found", "NOT_FOUND", 404)

class ExternalServiceError(AppError):
    def __init__(self, service: str, cause: Exception | None = None):
        super().__init__(f"External service failure: {service}",
                        "EXTERNAL_SERVICE_ERROR", 502)
        self.__cause__ = cause

Operational vs. Programmer Errors:

  • Operational errors (expected): Invalid input, network timeout, resource not found. Handle gracefully.
  • Programmer errors (bugs): Null dereference, type error, assertion failure. Let them crash and fix the code.

Step 3: Retry Policies

Design retry logic for transient failures:

Exponential backoff with jitter:

async function withRetry<T>(
  fn: () => Promise<T>,
  options: {
    maxRetries?: number;
    baseDelayMs?: number;
    maxDelayMs?: number;
    retryOn?: (error: Error) => boolean;
  } = {}
): Promise<T> {
  const {
    maxRetries = 3,
    baseDelayMs = 200,
    maxDelayMs = 10000,
    retryOn = isTransient,
  } = options;

  let lastError: Error;
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;
      if (attempt === maxRetries || !retryOn(lastError)) {
        throw lastError;
      }
      const delay = Math.min(
        baseDelayMs * Math.pow(2, attempt) + Math.random() * baseDelayMs,
        maxDelayMs
      );
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  throw lastError!;
}

function isTransient(error: Error): boolean {
  if (error instanceof RateLimitError) return true;
  if (error instanceof ExternalServiceError) return true;
  if ('statusCode' in error) {
    const code = (error as any).statusCode;
    return code === 429 || code === 502 || code === 503 || code === 504;
  }
  return false;
}

What to retry:

  • Network timeouts and connection errors
  • HTTP 429 (rate limited), 502, 503, 504 responses
  • Database connection failures
  • Message queue publish failures

What NOT to retry:

  • HTTP 400, 401, 403, 404, 422 (client errors, retrying will not help)
  • Validation errors
  • Business logic errors
  • Authentication failures

Step 4: Circuit Breaker Pattern

Prevent cascading failures when an external service is down:

class CircuitBreaker {
  private state: 'closed' | 'open' | 'half-open' = 'closed';
  private failureCount = 0;
  private lastFailureTime = 0;
  private successCount = 0;

  constructor(
    private readonly options: {
      failureThreshold: number;    // failures before opening
      resetTimeoutMs: number;      // time before trying again
      successThreshold: number;    // successes before closing
    }
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'open') {
      if (Date.now() - this.lastFailureTime > this.options.resetTimeoutMs) {
        this.state = 'half-open';
        this.successCount = 0;
      } else {
        throw new Error('Circuit breaker is open');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess(): void {
    if (this.state === 'half-open') {
      this.successCount++;
      if (this.successCount >= this.options.successThreshold) {
        this.state = 'closed';
        this.failureCount = 0;
      }
    } else {
      this.failureCount = 0;
    }
  }

  private onFailure(): void {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    if (this.failureCount >= this.options.failureThreshold) {
      this.state = 'open';
    }
  }
}

// Usage
const paymentCircuit = new CircuitBreaker({
  failureThreshold: 5,
  resetTimeoutMs: 30000,
  successThreshold: 3,
});

const result = await paymentCircuit.execute(() => paymentService.charge(order));

Step 5: Graceful Degradation

Design fallback behaviors when dependencies fail:

// Example: feature with fallback
async function getRecommendations(userId: string): Promise<Product[]> {
  try {
    // Primary: ML recommendation service
    return await recommendationService.getPersonalized(userId);
  } catch (error) {
    logger.warn('Recommendation service unavailable, falling back', { error });

    try {
      // Fallback 1: cached recommendations
      return await cache.get(`recommendations:${userId}`);
    } catch {
      // Fallback 2: popular items (always available)
      return await getPopularProducts();
    }
  }
}

Degradation levels:

  1. Full functionality: All services healthy, all features available.
  2. Reduced functionality: Non-critical service down, feature disabled with notification.
  3. Read-only mode: Write services down, reads still work from cache or replica.
  4. Maintenance mode: Critical failure, show status page, queue requests for replay.

Step 6: Error Reporting and Observability

Structured error logging:

// Global error handler for Express
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  const isOperational = err instanceof AppError && err.isOperational;

  // Log with structured context
  logger.error({
    error: {
      name: err.name,
      message: err.message,
      code: err instanceof AppError ? err.code : 'INTERNAL_ERROR',
      stack: err.stack,
    },
    request: {
      method: req.method,
      path: req.path,
      userId: req.user?.id,
      requestId: req.headers['x-request-id'],
    },
    operational: isOperational,
  });

  // Report to error tracking
  if (!isOperational) {
    errorTracker.captureException(err, {
      user: req.user,
      request: { method: req.method, url: req.url },
    });
  }

  // Send response
  const statusCode = err instanceof AppError ? err.statusCode : 500;
  res.status(statusCode).json({
    error: {
      code: err instanceof AppError ? err.code : 'INTERNAL_ERROR',
      message: isOperational ? err.message : 'An unexpected error occurred',
    },
  });
});

Never expose internal details to users:

  • Production error responses should not include stack traces.
  • Use error codes that the client can map to user-friendly messages.
  • Log the full error internally for debugging.

Output Format

## Error Handling Design

### Error Hierarchy
<class diagram or code for error classes>

### Error Response Format
```json
<standard error response shape>

Retry Policy

Error TypeRetry?Max AttemptsBackoffMax Delay

Circuit Breakers

ServiceFailure ThresholdReset TimeoutFallback

Degradation Strategy

DependencyImpact if DownFallback Behavior

Error Reporting

  • Logging: <structured logging setup>
  • Tracking: <error tracking integration>
  • Alerting: <alert rules for error rates>

Implementation Checklist

  • Error hierarchy implemented
  • Global error handler configured
  • Retry logic for external calls
  • Circuit breakers for critical dependencies
  • Structured logging with request context
  • Error tracking integration
  • User-facing error messages reviewed

## Examples

**User**: "Design error handling for our Node.js API"
**Action**: Read the project structure, design an error class hierarchy, create a global error handler, add retry logic for external service calls, configure structured logging.

**User**: "We keep getting timeout errors from the payment service"
**Action**: Implement a circuit breaker around the payment service, add retry with exponential backoff for transient failures, design a fallback (queue for later processing), set up alerting.

**User**: "Our API returns inconsistent error formats"
**Action**: Audit all error responses, design a standard error format (RFC 7807), create a global error handler that normalizes all errors, update endpoints to throw typed errors.

**User**: "How should I handle errors in my React app?"
**Action**: Implement Error Boundaries for component-level recovery, design a global error handler for unhandled promise rejections, create user-friendly error UI components, set up client-side error reporting.

## Best Practices

- Fail fast for programmer errors. Do not try to recover from null dereferences or type errors; let them crash and fix the bug.
- Handle operational errors gracefully. Network failures, invalid input, and missing resources are expected in production; the application should handle them without crashing.
- Use typed errors, not string messages, as the basis for error handling decisions. Checking `error.code === 'NOT_FOUND'` is reliable; checking `error.message.includes('not found')` is fragile.
- Always include context in error logs: request ID, user ID, operation name, and relevant parameters. An error message without context is useless for debugging.
- Never log sensitive data (passwords, tokens, PII) in error messages or context.
- Design retry policies per dependency, not globally. A payment service and a cache have very different retry needs.
- Set timeouts on every external call. A missing timeout is a potential resource leak waiting to happen.
- Use circuit breakers for external dependencies to prevent cascading failures. A dead service should not bring down your entire application.
- Test error paths as thoroughly as happy paths. Most production outages happen in error handling code that was never tested.
- Distinguish between errors the user can fix (validation, auth) and errors they cannot (internal failures). The error message and HTTP status code should reflect this.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.