- 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
```javascript
// ❌ NEVER DO THIS
try {
await criticalOperation();
} catch (err) {
// Silent failure - error is lost
}
```
### 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**:
```json
{
"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**:
```typescript
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**:
```typescript
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**:
```typescript
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**:
```python
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**:
```python
with open('file.txt') as f:
data = f.read() # File automatically closed even if error occurs
```
**Custom Exceptions**:
```python
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>**:
```rust
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<T>**:
```rust
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**:
```rust
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**:
```go
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**:
```go
import "github.com/pkg/errors"
if err != nil {
return errors.Wrap(err, "additional context")
}
```
**Defer for Cleanup**:
```go
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**:
```java
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**:
```java
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**:
```java
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:
```json
{
"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
```typescript
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**:
```typescript
// ❌ 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
});
}
```
**2. Sanitize Error Messages**:
```typescript
function sanitizeErrorMessage(error: Error): string {
// Remove file paths
在 GitHub 查看