# Role
You are a JavaScript Error Handling Expert who implements comprehensive error handling strategies with proper error boundaries, logging, user feedback, and recovery mechanisms.
# Task
Create a complete error handling system with error boundaries, logging infrastructure, user-friendly error messages, and recovery strategies.
# Instructions
**Application Context:**
**App Type:**
- Framework: [REACT_VUE_SVELTE_VANILLA]
- Environment: [BROWSER_NODE_BOTH]
- Error tracking service: [SENTRY_ROLLBAR_BUGSNAG_CUSTOM_NONE]
**Current Error Handling:**
```javascript
[PASTE_EXISTING_ERROR_HANDLING_CODE];
```
**Requirements:**
- Error boundary coverage: [FULL_PARTIAL_NONE]
- Logging needs: [LOCAL_REMOTE_BOTH]
- User feedback: [DETAILED_SIMPLE_MINIMAL]
- Recovery strategies: [AUTOMATIC_MANUAL_NONE]
Based on this information:
1. **Error Types and Classification:**
```typescript
// Base error class
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);
}
}
// Specific error types
class ValidationError extends AppError {
constructor(
message: string,
public field?: string
) {
super(message, 'VALIDATION_ERROR', 400);
}
}
class AuthenticationError extends AppError {
constructor(message: string = 'Authentication required') {
super(message, 'AUTH_ERROR', 401);
}
}
class AuthorizationError extends AppError {
constructor(message: string = 'Insufficient permissions') {
super(message, 'AUTHORIZATION_ERROR', 403);
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource} not found`, 'NOT_FOUND', 404);
}
}
class NetworkError extends AppError {
constructor(message: string = 'Network request failed') {
super(message, 'NETWORK_ERROR', 0);
}
}
```
2. **React Error Boundary:**
```typescript
import React, { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
hasError: false,
error: null
};
}
static getDerivedStateFromError(error: Error): State {
return {
hasError: true,
error
};
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// Log error to service
console.error('Error caught by boundary:', error, errorInfo);
// Call custom error handler
this.props.onError?.(error, errorInfo);
// Send to error tracking service
if (window.Sentry) {
window.Sentry.captureException(error, {
contexts: {
react: {
componentStack: errorInfo.componentStack
}
}
});
}
}
reset = () => {
this.setState({
hasError: false,
error: null
});
};
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
return (
<div className="error-boundary">
<h2>Something went wrong</h2>
<details>
<summary>Error details</summary>
<pre>{this.state.error?.message}</pre>
<pre>{this.state.error?.stack}</pre>
</details>
<button onClick={this.reset}>Try again</button>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
```
3. **Async Error Handling:**
```typescript
// Wrapper for async functions
function asyncHandler<T extends (...args: any[]) => Promise<any>>(fn: T): T {
return ((...args: Parameters<T>) => {
return Promise.resolve(fn(...args)).catch((error) => {
// Handle error
console.error('Async error:', error);
throw error;
});
}) as T;
}
// Usage
const fetchData = asyncHandler(async (id: string) => {
const response = await fetch(`/api/data/${id}`);
if (!response.ok) {
throw new NetworkError(`Failed to fetch data: ${response.statusText}`);
}
return response.json();
});
// Try-catch with proper typing
async function loadUser(id: string): Promise<User | null> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
if (response.status === 404) {
throw new NotFoundError('User');
}
throw new NetworkError();
}
return await response.json();
} catch (error) {
if (error instanceof AppError) {
// Handle known errors
console.error(error.code, error.message);
return null;
}
// Handle unknown errors
console.error('Unexpected error:', error);
throw error;
}
}
```
4. **Global Error Handler:**
```typescript
// Browser global error handler
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
// Send to error tracking
logError({
message: event.message,
source: event.filename,
line: event.lineno,
column: event.colno,
error: event.error
});
// Prevent default browser error handling
event.preventDefault();
});
// Unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
logError({
message: 'Unhandled promise rejection',
error: event.reason
});
event.preventDefault();
});
// Node.js global error handlers
process.on('uncaughtException', (error) => {
console.error('Uncaught exception:', error);
logError(error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled rejection at:', promise, 'reason:', reason);
logError(reason);
});
```
5. **Error Logging Service:**
```typescript
interface ErrorLog {
message: string;
stack?: string;
code?: string;
timestamp: number;
url: string;
userAgent: string;
userId?: string;
context?: Record<string, any>;
}
class ErrorLogger {
private queue: ErrorLog[] = [];
private endpoint: string;
constructor(endpoint: string) {
this.endpoint = endpoint;
this.setupFlush();
}
log(error: Error | AppError, context?: Record<string, any>) {
const errorLog: ErrorLog = {
message: error.message,
stack: error.stack,
code: error instanceof AppError ? error.code : 'UNKNOWN',
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent,
userId: this.getUserId(),
context
};
this.queue.push(errorLog);
// Flush immediately for critical errors
if (error instanceof AppError && !error.isOperational) {
this.flush();
}
}
private setupFlush() {
// Flush every 10 seconds
setInterval(() => this.flush(), 10000);
// Flush before page unload
window.addEventListener('beforeunload', () => this.flush());
}
private async flush() {
if (this.queue.length === 0) return;
const logs = [...this.queue];
this.queue = [];
try {
await fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(logs)
});
} catch (error) {
// Failed to send logs, add back to queue
this.queue.unshift(...logs);
}
}
private getUserId(): string | undefined {
// Get from auth state or localStorage
return localStorage.getItem('userId') || undefined;
}
}
export const errorLogger = new ErrorLogger('/api/errors');
```
6. **User-Friendly Error Messages:**
```typescript
const errorMessages: Record<string, string> = {
VALIDATION_ERROR: 'Please check your input and try again.',
AUTH_ERROR: 'Please log in to continue.',
AUTHORIZATION_ERROR: "You don't have permission to do that.",
NOT_FOUND: "We couldn't find what you're looking for.",
NETWORK_ERROR: 'Connection problem. Please check your internet and try again.',
SERVER_ERROR: "Something went wrong on our end. We're working on it.",
UNKNOWN: 'An unexpected error occurred. Please try again.'
};
function getUserMessage(error: Error | AppError): string {
if (error instanceof AppError) {
return errorMessages[error.code] || error.message;
}
return errorMessages.UNKNOWN;
}
// Toast notification component
function showErrorToast(error: Error) {
const message = getUserMessage(error);
// Show toast notification
toast.error(message, {
action: {
label: 'Retry',
onClick: () => {
// Retry logic
}
}
});
}
```
7. **Error Recovery Strategies:**
```typescript
// Retry with exponential backoff
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
let lastError: Error;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (i < maxRetries - 1) {
const delay = baseDelay * Math.pow(2, i);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
throw lastError!;
}
// Circuit breaker pattern
class CircuitBreaker {
private failureCount = 0;
private lastFailureTime = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
constructor(
private threshold: number = 5,
private timeout: number = 60000
) {}
async execute<T>(fn: () => 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 fn();
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';
}
}
}
```
8. **Form Validation Errors:**
```typescript
interface ValidationErrors {
[field: string]: string[];
}
function validateForm(data: Record<string, any>): ValidationErrors {
const errors: ValidationErrors = {};
if (!data.email) {
errors.email = ['Email is required'];
} else if (!isValidEmail(data.email)) {
errors.email = ['Please enter a valid email'];
}
if (!data.password) {
errors.password = ['Password is required'];
} else if (data.password.length < 8) {
errors.password = ['Password must be at least 8 characters'];
}
return errors;
}
// Display validation errors
function FormField({ name, errors }: { name: string; errors?: string[] }) {
return (
<div>
<input name={name} aria-invalid={!!errors} aria-describedby={`${name}-error`} />
{errors && (
<div id={`${name}-error`} role="alert">
{errors.map((error, i) => (
<p key={i}>{error}</p>
))}
</div>
)}
</div>
);
}
```
9. **Error Monitoring Integration:**
```typescript
// Sentry integration
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: 'YOUR_SENTRY_DSN',
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
beforeSend(event, hint) {
// Filter out non-operational errors
const error = hint.originalException;
if (error instanceof AppError && error.isOperational) {
return null;
}
return event;
}
});
// Add user context
Sentry.setUser({
id: userId,
email: userEmail
});
// Add breadcrumbs
Sentry.addBreadcrumb({
category: 'navigation',
message: 'User navigated to /dashboard',
level: 'info'
});
```
10. **Complete Error Handling System:**
Provide comprehensive error handling with:
- Error type definitions
- Error boundaries
- Global error handlers
- Logging infrastructure
- User-friendly messages
- Recovery strategies
- Monitoring integration
- Testing examples
Deliver a production-ready error handling system that catches errors gracefully, logs useful information, provides helpful user feedback, and includes recovery mechanisms.