Source profileQuality 86/100

affaan-m/ECC/skills/error-handling/SKILL.md

error-handling

Patterns for robust error handling across TypeScript, Python, and Go. Covers typed errors, error boundaries, retries, circuit breakers, and user-facing error messages.

Source repository stars
234,327
Declared platforms
0
Static risk flags
1
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Consistent, robust error handling patterns for production applications.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/error-handling"
    Safe inspection promptEditorial

    Inspect the Agent Skill "error-handling" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/error-handling/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      When to Activate

      Designing error types or exception hierarchies for a new module or service

      Designing error types or exception hierarchies for a new module or serviceAdding retry logic or circuit breakers for unreliable external dependenciesReviewing API endpoints for missing error handling
    2. 02

      Core Principles

      1. Fail fast and loudly — surface errors at the boundary where they occur; don't bury them 2. Typed errors over string messages — errors are first-class values with structure 3. User messages ≠ developer messages — show friendly text to users, log full context server-side 4. Nev…

      Fail fast and loudly — surface errors at the boundary where they occur; don't bury themTyped errors over string messages — errors are first-class values with structureUser messages ≠ developer messages — show friendly text to users, log full context server-side
    3. 03

      TypeScript / JavaScript

      For operations where failure is expected and common (parsing, external calls):

      For operations where failure is expected and common (parsing, external calls):
    4. 04

      Typed Error Classes

      Review the “Typed Error Classes” section in the pinned source before continuing.

      Review and apply the “Typed Error Classes” source section.
    5. 05

      Result Pattern (no-throw style)

      For operations where failure is expected and common (parsing, external calls):

      For operations where failure is expected and common (parsing, external calls):

    Permission review

    Static risk signals and limitations

    Network access

    medium · line 334

    The documentation includes network, browsing, or remote request actions.

    const data = await withRetry(() => fetch('/api/data').then(r => r.json()), {

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score86/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/error-handling/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Error Handling Patterns

    Consistent, robust error handling patterns for production applications.

    When to Activate

    • Designing error types or exception hierarchies for a new module or service
    • Adding retry logic or circuit breakers for unreliable external dependencies
    • Reviewing API endpoints for missing error handling
    • Implementing user-facing error messages and feedback
    • Debugging cascading failures or silent error swallowing

    Core Principles

    1. Fail fast and loudly — surface errors at the boundary where they occur; don't bury them
    2. Typed errors over string messages — errors are first-class values with structure
    3. User messages ≠ developer messages — show friendly text to users, log full context server-side
    4. Never swallow errors silently — every catch block must either handle, re-throw, or log
    5. Errors are part of your API contract — document every error code a client may receive

    TypeScript / JavaScript

    Typed Error Classes

    // Define an error hierarchy for your domain
    export class AppError extends Error {
      constructor(
        message: string,
        public readonly code: string,
        public readonly statusCode: number = 500,
        public readonly details?: unknown,
      ) {
        super(message)
        this.name = this.constructor.name
        // Maintain correct prototype chain in transpiled ES5 JavaScript.
        // Required for `instanceof` checks (e.g., `error instanceof NotFoundError`)
        // to work correctly when extending the built-in Error class.
        Object.setPrototypeOf(this, new.target.prototype)
      }
    }
    
    export class NotFoundError extends AppError {
      constructor(resource: string, id: string) {
        super(`${resource} not found: ${id}`, 'NOT_FOUND', 404)
      }
    }
    
    export class ValidationError extends AppError {
      constructor(message: string, details: { field: string; message: string }[]) {
        super(message, 'VALIDATION_ERROR', 422, details)
      }
    }
    
    export class UnauthorizedError extends AppError {
      constructor(reason = 'Authentication required') {
        super(reason, 'UNAUTHORIZED', 401)
      }
    }
    
    export class RateLimitError extends AppError {
      constructor(public readonly retryAfterMs: number) {
        super('Rate limit exceeded', 'RATE_LIMITED', 429)
      }
    }
    

    Result Pattern (no-throw style)

    For operations where failure is expected and common (parsing, external calls):

    type Result<T, E = AppError> =
      | { ok: true; value: T }
      | { ok: false; error: E }
    
    function ok<T>(value: T): Result<T> {
      return { ok: true, value }
    }
    
    function err<E>(error: E): Result<never, E> {
      return { ok: false, error }
    }
    
    // Usage
    async function fetchUser(id: string): Promise<Result<User>> {
      try {
        const user = await db.users.findUnique({ where: { id } })
        if (!user) return err(new NotFoundError('User', id))
        return ok(user)
      } catch (e) {
        return err(new AppError('Database error', 'DB_ERROR'))
      }
    }
    
    const result = await fetchUser('abc-123')
    if (!result.ok) {
      // TypeScript knows result.error here
      logger.error('Failed to fetch user', { error: result.error })
      return
    }
    // TypeScript knows result.value here
    console.log(result.value.email)
    

    API Error Handler (Next.js / Express)

    import { NextRequest, NextResponse } from 'next/server'
    
    function handleApiError(error: unknown): NextResponse {
      // Known application error
      if (error instanceof AppError) {
        return NextResponse.json(
          {
            error: {
              code: error.code,
              message: error.message,
              ...(error.details ? { details: error.details } : {}),
            },
          },
          { status: error.statusCode },
        )
      }
    
      // Zod validation error
      if (error instanceof z.ZodError) {
        return NextResponse.json(
          {
            error: {
              code: 'VALIDATION_ERROR',
              message: 'Request validation failed',
              details: error.issues.map(i => ({
                field: i.path.join('.'),
                message: i.message,
              })),
            },
          },
          { status: 422 },
        )
      }
    
      // Unexpected error — log details, return generic message
      console.error('Unexpected error:', error)
      return NextResponse.json(
        { error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } },
        { status: 500 },
      )
    }
    
    export async function POST(req: NextRequest) {
      try {
        // ... handler logic
      } catch (error) {
        return handleApiError(error)
      }
    }
    

    React Error Boundary

    import { Component, ErrorInfo, ReactNode } from 'react'
    
    interface Props {
      fallback: ReactNode
      onError?: (error: Error, info: ErrorInfo) => void
      children: ReactNode
    }
    
    interface State {
      hasError: boolean
      error: Error | null
    }
    
    export class ErrorBoundary extends Component<Props, State> {
      state: State = { hasError: false, error: null }
    
      static getDerivedStateFromError(error: Error): State {
        return { hasError: true, error }
      }
    
      componentDidCatch(error: Error, info: ErrorInfo) {
        this.props.onError?.(error, info)
        console.error('Unhandled React error:', error, info)
      }
    
      render() {
        if (this.state.hasError) return this.props.fallback
        return this.props.children
      }
    }
    
    // Usage
    <ErrorBoundary fallback={<p>Something went wrong. Please refresh.</p>}>
      <MyComponent />
    </ErrorBoundary>
    

    Python

    Custom Exception Hierarchy

    class AppError(Exception):
        """Base application error."""
        def __init__(self, message: str, code: str, status_code: int = 500):
            super().__init__(message)
            self.code = code
            self.status_code = status_code
    
    class NotFoundError(AppError):
        def __init__(self, resource: str, id: str):
            super().__init__(f"{resource} not found: {id}", "NOT_FOUND", 404)
    
    class ValidationError(AppError):
        def __init__(self, message: str, details: list[dict] | None = None):
            super().__init__(message, "VALIDATION_ERROR", 422)
            self.details = details or []
    

    FastAPI Global Exception Handler

    from fastapi import FastAPI, Request
    from fastapi.responses import JSONResponse
    
    app = FastAPI()
    
    @app.exception_handler(AppError)
    async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
        return JSONResponse(
            status_code=exc.status_code,
            content={"error": {"code": exc.code, "message": str(exc)}},
        )
    
    @app.exception_handler(Exception)
    async def generic_error_handler(request: Request, exc: Exception) -> JSONResponse:
        # Log full details, return generic message
        logger.exception("Unexpected error", exc_info=exc)
        return JSONResponse(
            status_code=500,
            content={"error": {"code": "INTERNAL_ERROR", "message": "An unexpected error occurred"}},
        )
    

    Go

    Sentinel Errors and Error Wrapping

    package domain
    
    import "errors"
    
    // Sentinel errors for type-checking
    var (
        ErrNotFound    = errors.New("not found")
        ErrUnauthorized = errors.New("unauthorized")
        ErrConflict     = errors.New("conflict")
    )
    
    // Wrap errors with context — never lose the original
    func (r *UserRepository) FindByID(ctx context.Context, id string) (*User, error) {
        user, err := r.db.QueryRow(ctx, "SELECT * FROM users WHERE id = $1", id)
        if errors.Is(err, sql.ErrNoRows) {
            return nil, fmt.Errorf("user %s: %w", id, ErrNotFound)
        }
        if err != nil {
            return nil, fmt.Errorf("querying user %s: %w", id, err)
        }
        return user, nil
    }
    
    // At the handler level, unwrap to determine response
    func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
        user, err := h.service.GetUser(r.Context(), chi.URLParam(r, "id"))
        if err != nil {
            switch {
            case errors.Is(err, domain.ErrNotFound):
                writeError(w, http.StatusNotFound, "not_found", err.Error())
            case errors.Is(err, domain.ErrUnauthorized):
                writeError(w, http.StatusForbidden, "forbidden", "Access denied")
            default:
                slog.Error("unexpected error", "err", err)
                writeError(w, http.StatusInternalServerError, "internal_error", "An unexpected error occurred")
            }
            return
        }
        writeJSON(w, http.StatusOK, user)
    }
    

    Retry with Exponential Backoff

    interface RetryOptions {
      maxAttempts?: number
      baseDelayMs?: number
      maxDelayMs?: number
      retryIf?: (error: unknown) => boolean
    }
    
    async function withRetry<T>(
      fn: () => Promise<T>,
      options: RetryOptions = {},
    ): Promise<T> {
      const {
        maxAttempts = 3,
        baseDelayMs = 500,
        maxDelayMs = 10_000,
        retryIf = () => true,
      } = options
    
      let lastError: unknown
    
      for (let attempt = 1; attempt <= maxAttempts; attempt++) {
        try {
          return await fn()
        } catch (error) {
          lastError = error
          if (attempt === maxAttempts || !retryIf(error)) throw error
    
          const jitter = Math.random() * baseDelayMs
          const delay = Math.min(baseDelayMs * 2 ** (attempt - 1) + jitter, maxDelayMs)
          await new Promise(resolve => setTimeout(resolve, delay))
        }
      }
    
      throw lastError
    }
    
    // Usage: retry transient network errors, not 4xx
    const data = await withRetry(() => fetch('/api/data').then(r => r.json()), {
      maxAttempts: 3,
      retryIf: (error) => !(error instanceof AppError && error.statusCode < 500),
    })
    

    User-Facing Error Messages

    Map error codes to human-readable messages. Keep technical details out of user-visible text.

    const USER_ERROR_MESSAGES: Record<string, string> = {
      NOT_FOUND: 'The requested item could not be found.',
      UNAUTHORIZED: 'Please sign in to continue.',
      FORBIDDEN: "You don't have permission to do that.",
      VALIDATION_ERROR: 'Please check your input and try again.',
      RATE_LIMITED: 'Too many requests. Please wait a moment and try again.',
      INTERNAL_ERROR: 'Something went wrong on our end. Please try again later.',
    }
    
    export function getUserMessage(code: string): string {
      return USER_ERROR_MESSAGES[code] ?? USER_ERROR_MESSAGES.INTERNAL_ERROR
    }
    

    Error Handling Checklist

    Before merging any code that touches error handling:

    • Every catch block handles, re-throws, or logs — no silent swallowing
    • API errors follow the standard envelope { error: { code, message } }
    • User-facing messages contain no stack traces or internal details
    • Full error context is logged server-side
    • Custom error classes extend a base AppError with a code field
    • Async functions surface errors to callers — no fire-and-forget without fallback
    • Retry logic only retries retriable errors (not 4xx client errors)
    • React components are wrapped in ErrorBoundary for rendering errors

    Alternatives

    Compare before choosing