Development
Comprehensive guidelines for handling errors gracefully across frontend and backend applications.
Copy this complete rule and save it as a .mdc file in your .cursor/rules directory
# Error Handling Standards
## Core Principles
1. **Fail Fast**: Detect and report errors as early as possible
2. **Fail Gracefully**: Always provide a fallback or recovery path
3. **Be Specific**: Use typed errors with clear messages
4. **Log Appropriately**: Capture context for debugging without leaking sensitive data
## Error Types
### Operational Errors
Expected errors that can be handled gracefully:
- Network failures
- Invalid user input
- Resource not found
- Permission denied
### Programming Errors
Bugs that should be fixed in code:
- Undefined variables
- Type mismatches
- Logic errors
## Implementation Patterns
### TypeScript/JavaScript
```typescript
// Custom error classes
class AppError 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);
}
}
class ValidationError extends AppError {
constructor(message: string, public field?: string) {
super(message, 'VALIDATION_ERROR', 400);
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource} not found`, 'NOT_FOUND', 404);
}
}
```
### Try-Catch Best Practices
```typescript
// DO: Catch specific errors
try {
await saveUser(data);
} catch (error) {
if (error instanceof ValidationError) {
return { error: error.message, field: error.field };
}
if (error instanceof NotFoundError) {
return { error: 'User not found' };
}
// Re-throw unexpected errors
throw error;
}
// DON'T: Catch and ignore
try {
await saveUser(data);
} catch (error) {
// Silent failure - BAD
}
```
### Async Error Handling
```typescript
// Promise chains
fetchData()
.then(processData)
.catch(handleError)
.finally(cleanup);
// Async/await with error boundary
async function fetchWithRetry(url: string, retries = 3): Promise<Response> {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url);
} catch (error) {
if (i === retries - 1) throw error;
await delay(1000 * Math.pow(2, i)); // Exponential backoff
}
}
throw new Error('Max retries exceeded');
}
```
## User-Facing Error Messages
### Guidelines
- Be helpful, not technical
- Suggest actions when possible
- Never expose internal details
- Maintain consistent tone
### Examples
```typescript
// BAD: Technical jargon
"Error: ECONNREFUSED 127.0.0.1:5432"
// GOOD: User-friendly
"Unable to save your changes. Please try again in a moment."
// BAD: Blame the user
"Invalid input in field email"
// GOOD: Helpful guidance
"Please enter a valid email address (e.g., [email protected])"
```
## Logging Standards
### What to Log
- Error message and code
- Stack trace (in development)
- Request context (sanitized)
- User action that triggered error
- Timestamp
### What NOT to Log
- Passwords or tokens
- Personal identifiable information (PII)
- Credit card numbers
- API keys or secrets
### Log Levels
- **ERROR**: Failures requiring immediate attention
- **WARN**: Potential issues or degraded functionality
- **INFO**: Important business events
- **DEBUG**: Detailed diagnostic information
## API Error Responses
```typescript
// Consistent error response format
interface ErrorResponse {
error: {
code: string;
message: string;
details?: Record<string, string[]>;
};
requestId?: string;
}
// Example response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request data",
"details": {
"email": ["Must be a valid email address"],
"password": ["Must be at least 8 characters"]
}
},
"requestId": "req_abc123"
}
```
## Recovery Strategies
1. **Retry with backoff**: For transient failures
2. **Circuit breaker**: Prevent cascade failures
3. **Fallback data**: Use cached or default values
4. **Graceful degradation**: Disable non-critical features
5. **User notification**: Inform and provide alternatives
What this rule helps you achieve