← Back to Skills Marketplace
wpank

API Error Handling

by wpank · GitHub ↗ · v1.0.0
cross-platform ✓ Security Clean
1401
Downloads
0
Stars
6
Active Installs
1
Versions
Install in OpenClaw
/install api-error-handling
Description
Error handling patterns across languages and layers — operational vs programmer errors, retry strategies, circuit breakers, error boundaries, HTTP responses, graceful degradation, and structured logging. Use when designing error strategies, building resilient APIs, or reviewing error management.
README (SKILL.md)

Error Handling Patterns

Ship resilient software. Handle errors at boundaries, fail fast and loud, never swallow exceptions silently.

Error Handling Philosophy

Principle Description
Fail Fast Detect errors early — validate inputs at the boundary, not deep in business logic
Fail Loud Errors must be visible — log them, surface them, alert on them
Handle at Boundaries Catch and translate errors at layer boundaries (controller, middleware, gateway)
Let It Crash For unrecoverable state, crash and restart (Erlang/OTP philosophy)
Be Specific Catch specific error types, never bare catch or except
Provide Context Every error carries enough context to diagnose without reproducing

Error Types

Operational errors — network timeouts, invalid user input, file not found, DB connection lost. Handle gracefully.

Programmer errorsTypeError, null dereference, assertion failures. Fix the code — don't catch and suppress.

// Operational — handle gracefully
try {
  const data = await fetch('/api/users');
} catch (err) {
  if (err.code === 'ECONNREFUSED') return fallbackData;
  throw err; // re-throw unexpected errors
}

// Programmer — let it crash, fix the bug
const user = null;
user.name; // TypeError — don't try/catch this

Language Patterns

Language Mechanism Anti-Pattern
JavaScript try/catch, Promise.catch, Error subclasses .catch(() => {}) swallowing errors
Python Exceptions, context managers (with) Bare except: catching everything
Go error returns, errors.Is/As, fmt.Errorf wrapping _ = riskyFunction() ignoring error
Rust Result\x3CT, E>, Option\x3CT>, ? operator .unwrap() in production code

JavaScript — Error Subclasses

class AppError extends Error {
  constructor(message, code, statusCode, details = {}) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.statusCode = statusCode;
    this.details = details;
    this.isOperational = true;
  }
}

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

class ValidationError extends AppError {
  constructor(errors) {
    super('Validation failed', 'VALIDATION_ERROR', 422, { errors });
  }
}

Go — Error Wrapping

func GetUser(id string) (*User, error) {
    row := db.QueryRow("SELECT * FROM users WHERE id = $1", id)
    var user User
    if err := row.Scan(&user.ID, &user.Name); err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, fmt.Errorf("user %s: %w", id, ErrNotFound)
        }
        return nil, fmt.Errorf("querying user %s: %w", id, err)
    }
    return &user, nil
}

Error Boundaries

Express Error Middleware

app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const response = {
    error: {
      code: err.code || 'INTERNAL_ERROR',
      message: err.isOperational ? err.message : 'Something went wrong',
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
      requestId: req.id,
    },
  };

  logger.error('Request failed', {
    err, requestId: req.id, method: req.method, path: req.path,
  });

  res.status(statusCode).json(response);
});

React Error Boundary

import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    \x3Cdiv role="alert">
      \x3Ch2>Something went wrong\x3C/h2>
      \x3Cpre>{error.message}\x3C/pre>
      \x3Cbutton onClick={resetErrorBoundary}>Try again\x3C/button>
    \x3C/div>
  );
}

\x3CErrorBoundary FallbackComponent={ErrorFallback} onReset={() => queryClient.clear()}>
  \x3CApp />
\x3C/ErrorBoundary>

Retry Patterns

Pattern When to Use Config
Exponential Backoff Transient failures (network, 503) Base 1s, max 30s, factor 2x
Backoff + Jitter Multiple clients retrying Random ±30% on each delay
Circuit Breaker Downstream service failing repeatedly Open after 5 failures, half-open after 30s
Bulkhead Isolate failures to prevent cascade Limit concurrent calls per service
Timeout Prevent indefinite hangs Connect 5s, read 30s, total 60s

Exponential Backoff with Jitter

async function withRetry(fn, { maxRetries = 3, baseDelay = 1000, maxDelay = 30000 } = {}) {
  for (let attempt = 0; attempt \x3C= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (attempt === maxRetries || !isRetryable(err)) throw err;
      const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
      const jitter = delay * (0.7 + Math.random() * 0.6);
      await new Promise((r) => setTimeout(r, jitter));
    }
  }
}

function isRetryable(err) {
  return [408, 429, 500, 502, 503, 504].includes(err.statusCode) || err.code === 'ECONNRESET';
}

Circuit Breaker

class CircuitBreaker {
  constructor({ threshold = 5, resetTimeout = 30000 } = {}) {
    this.state = 'CLOSED';       // CLOSED → OPEN → HALF_OPEN → CLOSED
    this.failureCount = 0;
    this.threshold = threshold;
    this.resetTimeout = resetTimeout;
    this.nextAttempt = 0;
  }

  async call(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() \x3C this.nextAttempt) throw new Error('Circuit is OPEN');
      this.state = 'HALF_OPEN';
    }
    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (err) {
      this.onFailure();
      throw err;
    }
  }

  onSuccess() { this.failureCount = 0; this.state = 'CLOSED'; }
  onFailure() {
    this.failureCount++;
    if (this.failureCount >= this.threshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.resetTimeout;
    }
  }
}

HTTP Error Responses

Status Name When to Use
400 Bad Request Malformed syntax, invalid JSON
401 Unauthorized Missing or invalid authentication
403 Forbidden Authenticated but insufficient permissions
404 Not Found Resource does not exist
409 Conflict Request conflicts with current state
422 Unprocessable Entity Valid syntax but semantic errors
429 Too Many Requests Rate limit exceeded (include Retry-After)
500 Internal Server Error Unexpected server failure
502 Bad Gateway Upstream returned invalid response
503 Service Unavailable Temporarily overloaded or maintenance

Standard Error Envelope

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The request body contains invalid fields.",
    "details": [
      { "field": "email", "message": "Must be a valid email address" }
    ],
    "requestId": "req_abc123xyz"
  }
}

Graceful Degradation

Strategy Example
Fallback values Show cached avatar when image service is down
Feature flags Disable unstable recommendation engine
Cached responses Serve stale data with X-Cache: STALE header
Partial response Return available data with warnings array
async function getProductPage(productId) {
  const product = await productService.get(productId); // critical — propagate errors

  const [reviews, recommendations] = await Promise.allSettled([
    reviewService.getForProduct(productId),
    recommendationService.getForProduct(productId),
  ]);

  return {
    product,
    reviews: reviews.status === 'fulfilled' ? reviews.value : [],
    recommendations: recommendations.status === 'fulfilled' ? recommendations.value : [],
    warnings: [reviews, recommendations]
      .filter((r) => r.status === 'rejected')
      .map((r) => ({ service: 'degraded', reason: r.reason.message })),
  };
}

Logging & Monitoring

Practice Implementation
Structured logging JSON: level, message, error, requestId, userId, timestamp
Error tracking Sentry, Datadog, Bugsnag — automatic capture with source maps
Alert thresholds Error rate > 1%, P99 latency > 2s, 5xx spike
Correlation IDs Pass requestId through all service calls
Log levels error = needs attention, warn = degraded, info = normal, debug = dev

Anti-Patterns

Anti-Pattern Fix
Swallowing errors catch (e) {} Log and re-throw, or handle explicitly
Generic catch-all at every level Catch specific types, let unexpected errors bubble
Error as control flow Use conditionals, return values, or option types
Stringly-typed errors throw "wrong" Throw Error objects with codes and context
Logging and throwing Log at the boundary only, or wrap and re-throw
Catch-and-return-null Return Result type, throw, or return error object
Ignoring Promise rejections Always await or attach .catch()
Exposing internals Sanitize responses; log details server-side only

NEVER Do

  1. NEVER swallow errors silentlycatch (e) {} hides bugs and causes silent data corruption
  2. NEVER expose stack traces, SQL errors, or file paths in API responses — log details server-side only
  3. NEVER use string throwsthrow 'error' has no stack trace, no type, no context
  4. NEVER catch and return null without explanation — callers have no idea why the operation failed
  5. NEVER ignore unhandled Promise rejections — always await or attach .catch()
  6. NEVER cache error responses — 5xx and transient errors must not be cached and re-served
  7. NEVER use exceptions for normal control flow — exceptions are for exceptional conditions
  8. NEVER return generic "Something went wrong" without logging the real error — always log the full error server-side with request context
Usage Guidance
This skill is documentation and example code for designing error-handling strategies and appears internally consistent. Before installing or running any copy commands from the README: prefer to read the SKILL.md locally; do not run unfamiliar shell commands (especially ones that copy from or to hidden directories) without inspecting them; the README's npx command is non-standard—if you choose to fetch from GitHub, use the official repository URL and review the files first. No credentials are required by the skill itself, so there is no immediate risk of credential exfiltration from the skill content.
Capability Analysis
Type: OpenClaw Skill Name: api-error-handling Version: 1.0.0 The skill bundle provides educational content on error handling patterns in software development. All code snippets are illustrative examples, and the markdown instructions are purely descriptive and advisory regarding error management best practices, with no evidence of prompt injection attempts against the AI agent. Installation instructions are standard for OpenClaw skills, involving `npx add` from a GitHub repository or local file copying, without any malicious execution or suspicious external dependencies.
Capability Assessment
Purpose & Capability
The name and description match the actual content: language-agnostic error-handling patterns, code examples, and guidance. The skill does not declare unrelated requirements (no env vars, binaries, or config paths).
Instruction Scope
SKILL.md is documentation and example code (Express middleware, React error boundaries, retry/circuit-breaker patterns). It does not instruct the agent to read arbitrary host files, access unexpected environment variables, or transmit data to external endpoints. Example references to process.env or req.id are normal sample code, not runtime directives to exfiltrate secrets.
Install Mechanism
There is no formal install spec in the skill bundle (lowest-risk form). README contains example installation snippets (npx add with a GitHub tree URL and copy commands from local ~/.ai-skills), which are non-standard and should be treated as documentation rather than an authoritative install method. This is not an active install in the bundle, but users should verify any external install commands before running them.
Credentials
The skill declares no required environment variables or credentials. Code samples reference common environment variables (e.g., NODE_ENV) only as examples; there is no disproportionate request for secrets or unrelated credentials.
Persistence & Privilege
The skill is not marked always:true and does not request persistent system presence or modify other skills' configs. Autonomous invocation (disable-model-invocation:false) is the platform default and not, by itself, a concern here.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install api-error-handling
  3. After installation, invoke the skill by name or use /api-error-handling
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
Initial release — comprehensive guide to error handling patterns across languages and system layers. - Details operational vs programmer errors and how to handle each. - Provides code examples for JavaScript, Python, Go, and Rust error handling best practices. - Includes error boundaries for Express (Node.js) and React. - Describes structured error responses for HTTP APIs and recommended status codes. - Outlines retry, backoff, circuit breaker, and graceful degradation strategies.
Metadata
Slug api-error-handling
Version 1.0.0
License
All-time Installs 6
Active Installs 6
Total Versions 1
Frequently Asked Questions

What is API Error Handling?

Error handling patterns across languages and layers — operational vs programmer errors, retry strategies, circuit breakers, error boundaries, HTTP responses, graceful degradation, and structured logging. Use when designing error strategies, building resilient APIs, or reviewing error management. It is an AI Agent Skill for Claude Code / OpenClaw, with 1401 downloads so far.

How do I install API Error Handling?

Run "/install api-error-handling" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is API Error Handling free?

Yes, API Error Handling is completely free (open-source). You can download, install and use it at no cost.

Which platforms does API Error Handling support?

API Error Handling is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created API Error Handling?

It is built and maintained by wpank (@wpank); the current version is v1.0.0.

💬 Comments