error-handling-skills

Error Handling Skills

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "error-handling-skills" with this command: npx skills add kimasplund/claude_cognitive_reasoning/kimasplund-claude-cognitive-reasoning-error-handling-skills

Error Handling Skills

Overview

This skill provides comprehensive error handling, exception management, and logging best practices applicable to all development work. It covers language-agnostic principles and language-specific implementations for JavaScript/TypeScript, Python, Rust, Go, and Java.

Use this skill when:

  • Implementing error handling in any application

  • Designing exception hierarchies

  • Setting up logging infrastructure

  • Handling failures and implementing recovery patterns

  • Securing error messages and stack traces

  • Testing error conditions

  • Debugging production issues

Core Error Handling Philosophy

  1. Fail Fast vs Graceful Degradation

Fail Fast - Immediately stop execution when an error occurs:

  • Use for: Critical errors, data corruption, security violations

  • Benefits: Prevents cascading failures, maintains data integrity

  • Example: Database connection failure, authentication breach, invalid configuration

Graceful Degradation - Continue operation with reduced functionality:

  • Use for: Non-critical features, external service failures, optional enhancements

  • Benefits: Better user experience, higher availability

  • Example: Analytics service down, search feature unavailable, image optimization failed

Decision Matrix:

Critical Path? → Yes → Fail Fast → No → Can provide fallback? → Yes → Graceful Degradation → No → Fail Fast with clear message

  1. Catch vs Propagate Errors

When to Catch (Handle Locally):

  • Can meaningfully recover from the error

  • Can provide a useful fallback value

  • Can add context before re-throwing

  • At API/system boundaries (convert internal errors to user-facing)

  • In retry/circuit breaker logic

When to Propagate (Let It Bubble):

  • Cannot recover or provide meaningful fallback

  • Error handling belongs to caller's responsibility

  • Preserving original error context is critical

  • In library code (let application decide handling)

Anti-Pattern: Catching and ignoring errors

// ❌ NEVER DO THIS try { await criticalOperation(); } catch (err) { // Silent failure - error is lost }

  1. Error Severity Levels

CRITICAL - System failure, immediate attention required:

  • Database down, service unreachable, security breach

  • Action: Alert on-call, page immediately, log to incident tracking

  • User message: "Service unavailable, we're working on it"

ERROR - Operation failed, manual intervention may be needed:

  • API request failed, file write failed, validation failed

  • Action: Log with full context, may trigger alerts if frequent

  • User message: Specific actionable message (e.g., "Invalid email format")

WARNING - Unexpected but handled condition:

  • Deprecated feature used, rate limit approaching, slow query

  • Action: Log for monitoring, no immediate action

  • User message: Usually none (internal only)

INFO - Normal operational events:

  • Request started/completed, user logged in, cache hit

  • Action: Log for audit/analytics

  • User message: None

DEBUG - Detailed diagnostic information:

  • Variable values, execution flow, intermediate states

  • Action: Log only in development/staging

  • User message: None

  1. Error Context and Stack Traces

Always Include:

  • Timestamp (ISO 8601 format)

  • Error type/code

  • User-facing message

  • Request ID / Correlation ID

  • User ID (if authenticated)

  • Operation being performed

  • Input parameters (sanitized)

Include in Logs Only (Never Expose to Users):

  • Full stack trace

  • Internal system details

  • File paths and line numbers

  • Database connection strings

  • Environment variables

Example Error Context:

{ "timestamp": "2025-11-14T10:30:45.123Z", "level": "ERROR", "error_type": "DatabaseConnectionError", "message": "Failed to connect to database", "request_id": "req_abc123", "user_id": "user_789", "operation": "create_order", "details": { "retry_count": 3, "last_error": "Connection timeout after 5000ms" }, "stack_trace": "..." // Internal only }

Error Handling Patterns by Language

This section provides quick-reference patterns. For detailed implementations and examples, see the language-specific reference files:

  • references/javascript-patterns.md

  • JavaScript/TypeScript detailed patterns

  • references/python-patterns.md

  • Python detailed patterns

  • references/rust-patterns.md

  • Rust detailed patterns

  • references/go-patterns.md

  • Go detailed patterns

  • references/java-patterns.md

  • Java detailed patterns

JavaScript/TypeScript Quick Reference

Synchronous Errors:

try { const result = riskyOperation(); return result; } catch (error) { if (error instanceof ValidationError) { return handleValidationError(error); } throw error; // Propagate unknown errors } finally { cleanup(); // Always runs }

Async/Await Errors:

try { const data = await fetchData(); return processData(data); } catch (error) { logger.error('Data fetch failed', { error, requestId }); throw new ServiceError('Unable to fetch data', { cause: error }); }

Promise Rejection:

fetchData() .then(processData) .catch(error => { logger.error('Pipeline failed', { error }); return fallbackData; // Graceful degradation });

See references/javascript-patterns.md for custom error classes, async error boundaries, and Express/Nest.js patterns.

Python Quick Reference

Try-Except-Finally:

try: result = risky_operation() return result except ValueError as e: logger.error(f"Validation failed: {e}", exc_info=True) raise ValidationError(f"Invalid input: {e}") from e except Exception as e: logger.critical(f"Unexpected error: {e}", exc_info=True) raise finally: cleanup() # Always runs

Context Managers:

with open('file.txt') as f: data = f.read() # File automatically closed even if error occurs

Custom Exceptions:

class ApplicationError(Exception): """Base exception for application errors""" pass

class DatabaseError(ApplicationError): """Database operation failed""" pass

See references/python-patterns.md for exception chaining, decorators, and FastAPI/Django patterns.

Rust Quick Reference

Result<T, E>:

fn read_file(path: &str) -> Result<String, std::io::Error> { std::fs::read_to_string(path) }

// Using ?operator to propagate fn process() -> Result<(), Box<dyn std::error::Error>> { let content = read_file("config.toml")?; Ok(()) }

Option:

fn find_user(id: u32) -> Option<User> { database.get(id) }

// Using unwrap_or for fallback let user = find_user(123).unwrap_or_default();

Custom Errors with thiserror:

use thiserror::Error;

#[derive(Error, Debug)] pub enum AppError { #[error("Database error: {0}")] Database(#[from] sqlx::Error),

#[error("Validation failed: {0}")]
Validation(String),

}

See references/rust-patterns.md for panic vs Result, error conversion, and anyhow patterns.

Go Quick Reference

Error Interface:

func readFile(path string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read %s: %w", path, err) } return data, nil }

Error Wrapping:

import "github.com/pkg/errors"

if err != nil { return errors.Wrap(err, "additional context") }

Defer for Cleanup:

func process() error { f, err := os.Open("file.txt") if err != nil { return err } defer f.Close() // Runs when function exits

// Process file...
return nil

}

See references/go-patterns.md for custom error types, sentinel errors, and panic recovery.

Java Quick Reference

Try-Catch-Finally:

try { Result result = riskyOperation(); return result; } catch (ValidationException e) { logger.error("Validation failed", e); throw new ServiceException("Invalid input", e); } catch (Exception e) { logger.error("Unexpected error", e); throw e; } finally { cleanup(); // Always runs }

Try-with-Resources:

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) { return reader.readLine(); } catch (IOException e) { logger.error("File read failed", e); throw new ApplicationException("Unable to read file", e); } // Resource automatically closed

Custom Exceptions:

public class ApplicationException extends Exception { public ApplicationException(String message, Throwable cause) { super(message, cause); } }

See references/java-patterns.md for checked vs unchecked exceptions, exception hierarchies, and Spring Boot patterns.

Logging Best Practices

For comprehensive logging guidance, see references/logging-best-practices.md .

Log Levels Usage

CRITICAL: System-wide failure requiring immediate attention

Database connection pool exhausted Authentication service unreachable Configuration file corrupted

ERROR: Operation failed but system continues

API request failed after retries File upload failed validation Payment processing declined

WARNING: Unexpected condition that was handled

Using deprecated API endpoint Rate limit at 80% capacity Slow database query (>1s)

INFO: Normal operational events

User logged in successfully Order created: order_id=12345 Cache invalidated for key=users

DEBUG: Detailed diagnostic information

Query executed: SELECT * FROM users WHERE id=? Variable state: cart_items=[...] Function called: processPayment(amount=100.00)

Structured Logging Format

Use JSON format for production logs:

{ "timestamp": "2025-11-14T10:30:45.123Z", "level": "ERROR", "service": "order-service", "environment": "production", "request_id": "req_abc123", "user_id": "user_789", "operation": "create_order", "message": "Payment processing failed", "error_type": "PaymentDeclinedError", "error_code": "insufficient_funds", "duration_ms": 1250, "stack_trace": "...", "metadata": { "amount": 99.99, "currency": "USD", "payment_method": "card" } }

What to Log

ALWAYS Log:

  • Request start/completion with duration

  • Authentication events (login, logout, failures)

  • Authorization failures (access denied)

  • Data mutations (create, update, delete)

  • External service calls with response times

  • Error conditions with full context

NEVER Log:

  • Passwords or password hashes

  • API keys, tokens, secrets

  • Credit card numbers or CVV codes

  • Social security numbers

  • Private encryption keys

  • Session IDs or JWTs

  • Personal health information (PHI)

  • Any personally identifiable information (PII) unless required by compliance

Sanitization Pattern

function sanitizeForLogging(data: any): any { const sensitive = ['password', 'token', 'apiKey', 'secret', 'ssn', 'cvv']; const sanitized = { ...data };

for (const key of Object.keys(sanitized)) { if (sensitive.some(s => key.toLowerCase().includes(s))) { sanitized[key] = '[REDACTED]'; } }

return sanitized; }

logger.info('User created', sanitizeForLogging(userData));

Security Considerations

For detailed security guidance, see references/security-checklist.md .

Critical Security Rules

  1. Never Expose Internal Errors to Users:

// ❌ BAD - Exposes internal details catch (error) { res.status(500).json({ error: error.message, // Might contain sensitive paths stack: error.stack // Reveals code structure }); }

// ✅ GOOD - Generic user message, detailed internal logging catch (error) { logger.error('Database query failed', { error, query, userId }); res.status(500).json({ error: 'An unexpected error occurred. Please try again later.', errorId: requestId // User can reference this with support }); }

  1. Sanitize Error Messages:

function sanitizeErrorMessage(error: Error): string { // Remove file paths let message = error.message.replace(//[\w/]+.[\w]+/g, '[FILE]');

// Remove SQL queries message = message.replace(/SELECT .+ FROM/gi, '[QUERY]');

// Remove IP addresses message = message.replace(/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/g, '[IP]');

return message; }

  1. Rate Limit Error Responses:

// Prevent attackers from probing for vulnerabilities const errorRateLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 error responses per window message: 'Too many errors from this IP' });

app.use('/api/', errorRateLimiter);

  1. Different Messages for Different Audiences:

class ApplicationError extends Error { constructor( public userMessage: string, // Safe for users public internalMessage: string, // Detailed for logs public code: string ) { super(internalMessage); } }

// Usage throw new ApplicationError( 'Invalid credentials', // User sees this 'Password hash mismatch for user_id=123', // Logs show this 'AUTH_FAILED' );

Error Recovery Patterns

  1. Retry with Exponential Backoff

Use for transient failures (network issues, temporary service unavailability):

async function retryWithBackoff<T>( operation: () => Promise<T>, maxRetries: number = 3, baseDelay: number = 1000 ): Promise<T> { let lastError: Error;

for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error as Error;

  if (attempt &#x3C; maxRetries - 1) {
    const delay = baseDelay * Math.pow(2, attempt);
    logger.warn(`Retry attempt ${attempt + 1}/${maxRetries} after ${delay}ms`, { error });
    await new Promise(resolve => setTimeout(resolve, delay));
  }
}

}

throw new Error(Operation failed after ${maxRetries} retries: ${lastError.message}); }

// Usage const data = await retryWithBackoff(() => fetchFromAPI('/users'));

  1. Circuit Breaker Pattern

Prevent cascading failures by stopping requests to failing services:

class CircuitBreaker { private failureCount = 0; private lastFailureTime?: number; private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';

constructor( private threshold: number = 5, private timeout: number = 60000 // 1 minute ) {}

async execute<T>(operation: () => Promise<T>): Promise<T> { if (this.state === 'OPEN') { if (Date.now() - this.lastFailureTime! > this.timeout) { this.state = 'HALF_OPEN'; } else { throw new Error('Circuit breaker is OPEN'); } }

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

}

private onSuccess() { this.failureCount = 0; this.state = 'CLOSED'; }

private onFailure() { this.failureCount++; this.lastFailureTime = Date.now();

if (this.failureCount >= this.threshold) {
  this.state = 'OPEN';
  logger.error('Circuit breaker opened', { failureCount: this.failureCount });
}

} }

// Usage const breaker = new CircuitBreaker(); const data = await breaker.execute(() => fetchFromAPI('/users'));

  1. Fallback Values

Provide default values when operations fail:

async function getUserPreferences(userId: string): Promise<Preferences> { try { return await fetchPreferences(userId); } catch (error) { logger.warn('Failed to fetch preferences, using defaults', { userId, error }); return DEFAULT_PREFERENCES; } }

  1. Transaction Rollback

Ensure data consistency by rolling back on errors:

async function transferFunds(fromAccount: string, toAccount: string, amount: number) { const transaction = await db.beginTransaction();

try { await db.debit(fromAccount, amount, { transaction }); await db.credit(toAccount, amount, { transaction }); await transaction.commit(); logger.info('Transfer completed', { fromAccount, toAccount, amount }); } catch (error) { await transaction.rollback(); logger.error('Transfer failed, rolled back', { fromAccount, toAccount, amount, error }); throw new TransferError('Transfer failed', { cause: error }); } }

Custom Error Classes

When to Create Custom Errors

Create custom error classes when:

  • Errors need to be caught and handled differently

  • Errors need additional context or metadata

  • Building error hierarchies for different error categories

  • Providing domain-specific errors (e.g., OrderNotFoundError , PaymentDeclinedError )

Error Hierarchy Pattern

// Base application error class ApplicationError extends Error { constructor( message: string, public code: string, public statusCode: number = 500, public isOperational: boolean = true ) { super(message); this.name = this.constructor.name; Error.captureStackTrace(this, this.constructor); } }

// Domain-specific errors class ValidationError extends ApplicationError { constructor(message: string, public fields: Record<string, string>) { super(message, 'VALIDATION_ERROR', 400); } }

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

class UnauthorizedError extends ApplicationError { constructor(message: string = 'Unauthorized') { super(message, 'UNAUTHORIZED', 401); } }

// Usage if (!user) { throw new NotFoundError('User', userId); }

if (!isValid(data)) { throw new ValidationError('Invalid input', { email: 'Invalid format', age: 'Must be at least 18' }); }

Error Codes vs Error Types

Error Types (Class-based):

  • Use for programmatic error handling (catch specific errors)

  • Provides inheritance hierarchy

  • Better for typed languages

Error Codes (String-based):

  • Use for client-facing APIs

  • Easier to document and version

  • Language-agnostic

Best Practice: Use both

class PaymentError extends ApplicationError { constructor(code: string, message: string) { super(message, code, 402); } }

// Error codes enumeration enum PaymentErrorCode { INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS', INVALID_CARD = 'INVALID_CARD', EXPIRED_CARD = 'EXPIRED_CARD', DECLINED = 'DECLINED' }

throw new PaymentError(PaymentErrorCode.INSUFFICIENT_FUNDS, 'Insufficient funds');

Testing Error Paths

For comprehensive testing guidance, see testing-methodology-skills and references/testing-patterns.md .

Testing Error Conditions

Unit Tests:

describe('UserService', () => { it('should throw NotFoundError when user does not exist', async () => { mockDb.findUser.mockResolvedValue(null);

await expect(userService.getUser('invalid-id'))
  .rejects
  .toThrow(NotFoundError);

});

it('should log error when database fails', async () => { mockDb.findUser.mockRejectedValue(new Error('Connection failed'));

await expect(userService.getUser('user-123')).rejects.toThrow();
expect(mockLogger.error).toHaveBeenCalledWith(
  expect.stringContaining('Failed to fetch user'),
  expect.objectContaining({ userId: 'user-123' })
);

}); });

Mocking Errors:

// Mock external service failure jest.spyOn(externalApi, 'fetchData').mockRejectedValue( new Error('Service unavailable') );

// Mock network timeout jest.spyOn(httpClient, 'get').mockImplementation(() => new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 100) ) );

Error Coverage Checklist

Ensure tests cover:

  • ✅ Happy path (no errors)

  • ✅ Validation errors (invalid input)

  • ✅ Not found errors (missing resources)

  • ✅ Authentication/authorization errors

  • ✅ Network errors (timeout, connection refused)

  • ✅ External service failures

  • ✅ Database errors (connection, constraint violations)

  • ✅ Concurrent access errors (race conditions)

  • ✅ Rate limiting errors

  • ✅ Retry logic and exponential backoff

  • ✅ Circuit breaker state transitions

  • ✅ Transaction rollback on failure

  • ✅ Cleanup in finally/defer blocks

Common Error Handling Scenarios

Database Connection Error

class DatabaseError extends ApplicationError { constructor(operation: string, cause: Error) { super( Database operation failed: ${operation}, 'DATABASE_ERROR', 503 ); this.cause = cause; } }

async function queryDatabase<T>(query: string, params: any[]): Promise<T[]> { try { return await db.query(query, params); } catch (error) { logger.error('Database query failed', { query, params: sanitizeForLogging(params), error });

if (error.code === 'ECONNREFUSED') {
  throw new DatabaseError('Connection refused', error);
}

if (error.code === '23505') { // Unique constraint violation
  throw new ValidationError('Duplicate entry', { field: 'email' });
}

throw new DatabaseError(query, error);

} }

API Request Error

async function fetchFromAPI<T>(endpoint: string): Promise<T> { try { const response = await fetch(${API_BASE}${endpoint}, { headers: { 'Authorization': Bearer ${getToken()} }, timeout: 5000 });

if (!response.ok) {
  if (response.status === 401) {
    throw new UnauthorizedError('API authentication failed');
  }
  if (response.status === 404) {
    throw new NotFoundError('API Resource', endpoint);
  }
  if (response.status >= 500) {
    throw new ApplicationError(
      'API server error',
      'API_ERROR',
      response.status
    );
  }
  throw new ApplicationError(
    'API request failed',
    'API_ERROR',
    response.status
  );
}

return await response.json();

} catch (error) { if (error instanceof ApplicationError) { throw error; }

// Network errors
logger.error('API request failed', { endpoint, error });
throw new ApplicationError(
  'Unable to connect to API',
  'NETWORK_ERROR',
  503
);

} }

File I/O Error

async function readConfigFile(path: string): Promise<Config> { try { const content = await fs.readFile(path, 'utf-8'); return JSON.parse(content); } catch (error) { if (error.code === 'ENOENT') { logger.warn('Config file not found, using defaults', { path }); return DEFAULT_CONFIG; }

if (error instanceof SyntaxError) {
  logger.error('Invalid JSON in config file', { path, error });
  throw new ValidationError('Config file is not valid JSON', { path });
}

logger.error('Failed to read config file', { path, error });
throw new ApplicationError('Unable to read configuration', 'CONFIG_ERROR');

} }

Authentication Error

async function authenticateUser(email: string, password: string): Promise<User> { try { const user = await db.findUserByEmail(email);

if (!user) {
  // Don't reveal whether email exists
  logger.warn('Login attempt for non-existent user', { email });
  throw new UnauthorizedError('Invalid credentials');
}

const isValid = await bcrypt.compare(password, user.passwordHash);

if (!isValid) {
  logger.warn('Invalid password attempt', { userId: user.id, email });
  await incrementFailedLoginAttempts(user.id);
  throw new UnauthorizedError('Invalid credentials');
}

if (user.isLocked) {
  logger.warn('Login attempt for locked account', { userId: user.id });
  throw new UnauthorizedError('Account is locked');
}

logger.info('User authenticated successfully', { userId: user.id });
return user;

} catch (error) { if (error instanceof UnauthorizedError) { throw error; }

logger.error('Authentication error', { email, error });
throw new ApplicationError('Authentication failed', 'AUTH_ERROR');

} }

Validation Error

interface ValidationResult { isValid: boolean; errors: Record<string, string>; }

function validateUserInput(data: any): ValidationResult { const errors: Record<string, string> = {};

if (!data.email || !isValidEmail(data.email)) { errors.email = 'Valid email is required'; }

if (!data.password || data.password.length < 8) { errors.password = 'Password must be at least 8 characters'; }

if (!data.age || data.age < 18) { errors.age = 'Must be at least 18 years old'; }

return { isValid: Object.keys(errors).length === 0, errors }; }

async function createUser(data: any): Promise<User> { const validation = validateUserInput(data);

if (!validation.isValid) { logger.warn('User validation failed', { errors: validation.errors }); throw new ValidationError('Invalid user data', validation.errors); }

try { return await db.createUser(data); } catch (error) { logger.error('Failed to create user', { data: sanitizeForLogging(data), error }); throw new DatabaseError('create user', error); } }

References

This skill includes detailed reference documentation:

  • references/javascript-patterns.md - JavaScript/TypeScript error handling patterns, async error boundaries, Express/Nest.js integration

  • references/python-patterns.md - Python exception patterns, decorators, context managers, FastAPI/Django integration

  • references/rust-patterns.md - Rust Result/Option patterns, thiserror/anyhow usage, panic handling

  • references/go-patterns.md - Go error interface patterns, error wrapping, panic/recover

  • references/java-patterns.md - Java exception hierarchy, checked vs unchecked, Spring Boot integration

  • references/security-checklist.md - Comprehensive security checklist for error handling

  • references/logging-best-practices.md - Detailed logging patterns, structured logging, log aggregation

Load these references when working with specific languages or needing detailed implementation guidance.

Quick Decision Tree

Error Occurred ├─ Can recover meaningfully? │ ├─ Yes → Handle locally (try-catch) │ │ └─ Log with context │ │ └─ Return fallback or retry │ └─ No → Propagate to caller │ └─ Add context if helpful │ └─ Let higher level decide │ ├─ User-facing error? │ ├─ Yes → Generic message + error ID │ │ └─ Log detailed error internally │ └─ No → Detailed error message OK │ ├─ Transient failure? │ ├─ Yes → Retry with backoff │ └─ No → Fail immediately │ └─ Critical system error? ├─ Yes → Fail fast + alert └─ No → Graceful degradation

Summary

Error handling is not an afterthought—it's a critical part of building reliable, secure, and maintainable systems. Follow these principles:

  • Be Intentional: Decide whether to catch or propagate based on whether you can meaningfully recover

  • Provide Context: Always log errors with full context (request ID, user ID, operation, parameters)

  • Secure by Default: Never expose internal errors to users, sanitize all error messages

  • Test Error Paths: Error handling code is code—test it thoroughly

  • Monitor and Alert: Use structured logging and monitoring to catch issues before users do

  • Fail Gracefully: When possible, degrade gracefully rather than failing completely

  • Learn from Errors: Use error logs to identify patterns and improve system reliability

Use the language-specific reference files for detailed implementations and examples.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

integrated-reasoning

No summary provided by upstream source.

Repository SourceNeeds Review
General

chromadb-integration-skills

No summary provided by upstream source.

Repository SourceNeeds Review
General

document-writing-skills

No summary provided by upstream source.

Repository SourceNeeds Review
General

confidence-check-skills

No summary provided by upstream source.

Repository SourceNeeds Review