| name | error-handling-skills |
| description | Universal error handling, exception management, and logging best practices for all development agents across JavaScript/TypeScript, Python, Rust, Go, and Java. Use when implementing error handling, exception management, logging, error recovery, or debugging production issues. |
| license | MIT |
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
2. 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
try {
await criticalOperation();
} catch (err) {
}
3. 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
4. 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": "..."
}
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;
} finally {
cleanup();
}
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;
});
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()
Context Managers:
with open('file.txt') as f:
data = f.read()
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)
}
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)
}
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()
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();
}
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);
}
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:
catch (error) {
res.status(500).json({
error: error.message,
stack: error.stack
});
}
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
});
}
2. Sanitize Error Messages:
function sanitizeErrorMessage(error: Error): string {