Error Handling Prompt Templates

AI prompt templates for error handling. Implement robust exception management and error recovery.

Overview

Error handling separates solid applications from fragile ones. These prompts help you design error handling strategies that catch failures gracefully, provide useful feedback to users and developers, and enable recovery when possible. The goal is code that fails predictably and informatively, not silently or catastrophically.

Best Practices

1

Describe the types of errors you expect (validation, network, business logic) so handling can be specific

2

Specify your target audience for error messages, developers need stack traces, users need friendly messages

3

Include your current error handling patterns so new code stays consistent

4

Mention whether errors should be recoverable or terminal for your use case

5

Consider logging, monitoring, and alerting needs alongside the error handling code

Prompt Templates

1. Error Handling Strategy

Design an error handling strategy for this code/feature:

**Context:**
- Application type: [WEB APP / API / CLI / LIBRARY]
- Language/Framework: [LANGUAGE AND FRAMEWORK]
- Feature: [WHAT THE CODE DOES]

**Error types to handle:**
- [ERROR TYPE 1 - e.g. validation errors]
- [ERROR TYPE 2 - e.g. network failures]
- [ERROR TYPE 3 - e.g. business rule violations]

**Error consumers:**
- End users: [WHAT THEY SHOULD SEE]
- Developers: [WHAT THEY NEED FOR DEBUGGING]
- Monitoring: [WHAT TO LOG/ALERT ON]

**Current code (if exists):**
```[LANGUAGE]
[PASTE RELEVANT CODE]
```

**Provide:**
1. Error classification scheme
2. Custom error classes/types if needed
3. Error handling patterns for each error type
4. User-facing message strategy
5. Logging and monitoring approach
Application: REST API for e-commerce
Language: Node.js/Express
Feature: Order checkout process
Error types: Validation (bad input), Payment failures, Inventory issues, External service timeouts
Users see: Friendly messages with action steps
Developers need: Full context for debugging
Monitoring: Alert on payment failures and service timeouts
## Error Handling Strategy: Order Checkout

### 1. Error Classification

```typescript
// Base error class
export class AppError extends Error {
 constructor(
 message: string,
 public code: string,
 public statusCode: number,
 public isOperational: boolean = true,
 public context?: Record<string, unknown>
 ) {
 super(message);
 this.name = this.constructor.name;
 Error.captureStackTrace(this, this.constructor);
 }
}

// Specific error types
export class ValidationError extends AppError {
 constructor(message: string, public fields: FieldError[]) {
 super(message, 'VALIDATION_ERROR', 400, true, { fields });
 }
}

export class PaymentError extends AppError {
 constructor(message: string, public declineCode?: string) {
 super(message, 'PAYMENT_FAILED', 402, true, { declineCode });
 }
}

export class InventoryError extends AppError {
 constructor(message: string, public productId: string, public available: number) {
 super(message, 'INSUFFICIENT_INVENTORY', 409, true, { productId, available });
 }
}

export class ExternalServiceError extends AppError {
 constructor(service: string, originalError: Error) {
 super(
 `External service failure: ${service}`,
 'EXTERNAL_SERVICE_ERROR',
 503,
 true,
 { service, originalMessage: originalError.message }
 );
 }
}
```

### 2. Error Handling Patterns

```typescript
// Checkout service with structured error handling
export class CheckoutService {
 async processCheckout(order: OrderInput): Promise<Order> {
 // Validation errors - fail fast
 const validation = this.validateOrder(order);
 if (!validation.valid) {
 throw new ValidationError('Invalid order data', validation.errors);
 }

 // Inventory check - recoverable if user adjusts
 for (const item of order.items) {
 const available = await this.inventory.getAvailable(item.productId);
 if (available < item.quantity) {
 throw new InventoryError(
 `Insufficient stock for ${item.productId}`,
 item.productId,
 available
 );
 }
 }

 // Payment - may require user action
 try {
 const payment = await this.paymentService.charge(order);
 if (!payment.success) {
 throw new PaymentError(
 'Payment was declined',
 payment.declineCode
 );
 }
 } catch (error) {
 if (error instanceof PaymentError) throw error;
 // Wrap unexpected payment errors
 throw new ExternalServiceError('payment-processor', error);
 }

 // External service calls - wrap with timeout and retry
 try {
 await this.withRetry(() => this.shippingService.createLabel(order));
 } catch (error) {
 throw new ExternalServiceError('shipping-service', error);
 }

 return this.orderRepository.save(order);
 }

 private async withRetry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
 for (let i = 0; i < attempts; i++) {
 try {
 return await fn();
 } catch (error) {
 if (i === attempts - 1) throw error;
 await this.delay(Math.pow(2, i) * 1000); // Exponential backoff
 }
 }
 throw new Error('Retry failed');
 }
}
```

### 3. Express Error Middleware

```typescript
// Global error handler
export function errorHandler(
 error: Error,
 req: Request,
 res: Response,
 next: NextFunction
) {
 // Log all errors with context
 const logContext = {
 requestId: req.id,
 method: req.method,
 path: req.path,
 userId: req.user?.id,
 error: {
 name: error.name,
 message: error.message,
 stack: error.stack...(error instanceof AppError && { code: error.code, context: error.context })
 }
 };

 if (error instanceof AppError) {
 // Operational error - expected, log as warning
 logger.warn('Operational error', logContext);

 // Alert on specific error types
 if (error instanceof PaymentError || error instanceof ExternalServiceError) {
 alerting.notify('checkout-error', logContext);
 }

 return res.status(error.statusCode).json({
 error: {
 code: error.code,
 message: this.getUserMessage(error)...(error instanceof ValidationError && { fields: error.fields })...(error instanceof InventoryError && { 
 available: error.available,
 productId: error.productId 
 })
 }
 });
 }

 // Unexpected error - log as error, return generic message
 logger.error('Unexpected error', logContext);
 alerting.page('unexpected-error', logContext); // Wake someone up

 return res.status(500).json({
 error: {
 code: 'INTERNAL_ERROR',
 message: 'Something went wrong. Please try again or contact support.',
 requestId: req.id // For support tickets
 }
 });
}

// User-friendly messages
function getUserMessage(error: AppError): string {
 const messages: Record<string, string> = {
 VALIDATION_ERROR: 'Please check your information and try again.',
 PAYMENT_FAILED: 'Your payment could not be processed. Please try a different payment method.',
 INSUFFICIENT_INVENTORY: 'Some items in your cart are no longer available in the requested quantity.',
 EXTERNAL_SERVICE_ERROR: 'We\'re experiencing technical difficulties. Please try again in a few minutes.'
 };
 return messages[error.code] || 'An error occurred. Please try again.';
}
```

### 4. Frontend Error Handling

```typescript
// API client with error normalization
async function checkout(order: OrderInput): Promise<Order> {
 try {
 const response = await api.post('/checkout', order);
 return response.data;
 } catch (error) {
 if (error.response) {
 const { code, message, fields } = error.response.data.error;
 
 switch (code) {
 case 'VALIDATION_ERROR':
 // Show field-level errors
 throw new ValidationError(message, fields);
 case 'INSUFFICIENT_INVENTORY':
 // Prompt user to update cart
 throw new InventoryError(message, error.response.data.error);
 case 'PAYMENT_FAILED':
 // Show payment retry UI
 throw new PaymentError(message);
 default:
 // Generic error with retry option
 throw new CheckoutError(message);
 }
 }
 // Network error
 throw new NetworkError('Unable to connect. Please check your internet connection.');
 }
}
```

### 5. Monitoring & Alerting

| Error Type | Log Level | Alert |
|------------|-----------|-------|
| ValidationError | warn | No (user error) |
| PaymentError | warn | Yes (Slack) if rate >5% |
| InventoryError | warn | No (expected) |
| ExternalServiceError | error | Yes (PagerDuty) |
| Unexpected errors | error | Yes (PagerDuty) |

When designing error handling for new features, standardizing across a codebase, or improving existing error handling

  • Distinguish operational errors (expected) from programmer errors (bugs), handle them differently
  • Include request IDs in error responses so users can reference them in support tickets

2. Try-Catch Improvement

Improve the error handling in this code:

```[LANGUAGE]
[PASTE CODE WITH POOR ERROR HANDLING]
```

**Current problems:**
- [PROBLEM 1 - e.g. catching all errors generically]
- [PROBLEM 2 - e.g. swallowing errors silently]

**Requirements:**
- [WHAT SHOULD HAPPEN ON EACH ERROR TYPE]
- [LOGGING REQUIREMENTS]
- [USER FEEDBACK REQUIREMENTS]

**Provide:**
1. Improved code with proper error handling
2. Explanation of what changed and why
3. Any custom error types needed
Language: Python
Code: Function that reads config, calls API, saves to database
Problems: Single try/catch around everything, generic 'Error occurred' message, no logging
Requirements: Different handling for file errors vs API errors vs DB errors, log errors with context, return specific error messages
## Improved Error Handling

### Before (problematic)
```python
def sync_user_data(user_id):
 try:
 config = load_config('config.json')
 data = fetch_from_api(config['api_url'], user_id)
 save_to_database(data)
 return {'success': True}
 except:
 return {'success': False, 'error': 'Error occurred'}
```

### After (improved)
```python
import logging
from dataclasses import dataclass
from typing import Optional

logger = logging.getLogger(__name__)

# Custom exceptions for each error category
class ConfigError(Exception):
 """Configuration file errors."""
 pass

class APIError(Exception):
 """External API errors."""
 def __init__(self, message: str, status_code: Optional[int] = None):
 super().__init__(message)
 self.status_code = status_code

class DatabaseError(Exception):
 """Database operation errors."""
 pass

@dataclass
class SyncResult:
 success: bool
 error_code: Optional[str] = None
 error_message: Optional[str] = None
 user_message: Optional[str] = None

def sync_user_data(user_id: str) -> SyncResult:
 """
 Sync user data from external API to database.
 
 Returns SyncResult with success status and error details if failed.
 """
 # Step 1: Load configuration
 try:
 config = load_config('config.json')
 except FileNotFoundError:
 logger.error("Config file not found", extra={'user_id': user_id})
 return SyncResult(
 success=False,
 error_code='CONFIG_NOT_FOUND',
 error_message='Configuration file missing',
 user_message='Service is misconfigured. Please contact support.'
 )
 except json.JSONDecodeError as e:
 logger.error("Config file invalid JSON", extra={'user_id': user_id, 'error': str(e)})
 return SyncResult(
 success=False,
 error_code='CONFIG_INVALID',
 error_message=f'Configuration parse error: {e}',
 user_message='Service is misconfigured. Please contact support.'
 )

 # Step 2: Fetch from API
 try:
 data = fetch_from_api(config['api_url'], user_id)
 except requests.Timeout:
 logger.warning("API timeout", extra={'user_id': user_id, 'url': config['api_url']})
 return SyncResult(
 success=False,
 error_code='API_TIMEOUT',
 error_message='API request timed out',
 user_message='The service is taking too long. Please try again.'
 )
 except requests.HTTPError as e:
 logger.error("API HTTP error", extra={
 'user_id': user_id,
 'status_code': e.response.status_code,
 'response': e.response.text[:500]
 })
 if e.response.status_code == 404:
 return SyncResult(
 success=False,
 error_code='USER_NOT_FOUND',
 error_message=f'User {user_id} not found in external system',
 user_message='Your account was not found. Please verify your information.'
 )
 return SyncResult(
 success=False,
 error_code='API_ERROR',
 error_message=f'API returned {e.response.status_code}',
 user_message='Unable to fetch your data. Please try again later.'
 )
 except requests.RequestException as e:
 logger.error("API connection error", extra={'user_id': user_id, 'error': str(e)})
 return SyncResult(
 success=False,
 error_code='API_CONNECTION_ERROR',
 error_message=str(e),
 user_message='Unable to connect to external service. Please try again.'
 )

 # Step 3: Save to database
 try:
 save_to_database(data)
 except IntegrityError as e:
 logger.error("Database integrity error", extra={'user_id': user_id, 'error': str(e)})
 return SyncResult(
 success=False,
 error_code='DB_INTEGRITY_ERROR',
 error_message=f'Data conflict: {e}',
 user_message='There was a conflict saving your data. Please contact support.'
 )
 except OperationalError as e:
 logger.error("Database connection error", extra={'user_id': user_id, 'error': str(e)})
 return SyncResult(
 success=False,
 error_code='DB_CONNECTION_ERROR',
 error_message=str(e),
 user_message='Database temporarily unavailable. Please try again.'
 )

 logger.info("User data synced successfully", extra={'user_id': user_id})
 return SyncResult(success=True)
```

### What Changed

| Before | After | Why |
|--------|-------|-----|
| Bare `except:` | Specific exception types | Catch only what you can handle |
| Generic error message | Error codes + user messages | Actionable for users and debugging |
| No logging | Structured logging with context | Enables debugging and monitoring |
| Returns dict | Returns dataclass | Type safety, clear contract |
| All-or-nothing | Fail at each step independently | Identify exactly where failure occurred |

When improving existing code with poor error handling, code review feedback, or debugging mysterious failures

  • Never use bare 'except:' or 'catch (Exception e)' unless you re-raise, it hides bugs
  • Separate error messages for developers (detailed) from users (actionable, no internals)

3. Retry Logic Implementation

Implement retry logic for this operation:

**Operation:**
```[LANGUAGE]
[PASTE CODE THAT NEEDS RETRY]
```

**Requirements:**
- Retry on: [WHICH ERRORS SHOULD TRIGGER RETRY]
- Don't retry on: [WHICH ERRORS SHOULD FAIL IMMEDIATELY]
- Max attempts: [NUMBER]
- Backoff strategy: [FIXED / EXPONENTIAL / JITTER]
- Timeout: [PER-ATTEMPT AND TOTAL]

**Provide:**
1. Retry implementation
2. Configuration options
3. Logging for observability
4. Example usage
Operation: HTTP API call to payment processor
Retry on: 5xx errors, timeouts, connection errors
Don't retry on: 4xx errors (client errors), invalid input
Max attempts: 3
Backoff: Exponential with jitter
Timeout: 10s per attempt, 30s total
## Retry Logic Implementation

```typescript
import { setTimeout } from 'timers/promises';

// Configuration
interface RetryConfig {
 maxAttempts: number;
 baseDelayMs: number;
 maxDelayMs: number;
 timeoutMs: number;
 totalTimeoutMs: number;
 jitter: boolean;
}

const DEFAULT_CONFIG: RetryConfig = {
 maxAttempts: 3,
 baseDelayMs: 1000,
 maxDelayMs: 10000,
 timeoutMs: 10000,
 totalTimeoutMs: 30000,
 jitter: true,
};

// Errors that should trigger retry
function isRetryable(error: unknown): boolean {
 if (error instanceof ApiError) {
 // Retry on 5xx (server errors) and specific network errors
 return (
 error.statusCode >= 500 ||
 error.code === 'ETIMEDOUT' ||
 error.code === 'ECONNRESET' ||
 error.code === 'ECONNREFUSED'
 );
 }
 // Network errors without response
 if (error instanceof TypeError && error.message.includes('fetch')) {
 return true;
 }
 return false;
}

// Exponential backoff with jitter
function calculateDelay(attempt: number, config: RetryConfig): number {
 // Exponential: 1s, 2s, 4s, 8s...
 const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
 const cappedDelay = Math.min(exponentialDelay, config.maxDelayMs);
 
 if (config.jitter) {
 // Add random jitter: 50-150% of calculated delay
 return cappedDelay * (0.5 + Math.random());
 }
 return cappedDelay;
}

// Main retry function
async function withRetry<T>(
 operation: () => Promise<T>,
 config: Partial<RetryConfig> = {}
): Promise<T> {
 const finalConfig = {...DEFAULT_CONFIG...config };
 const startTime = Date.now();
 let lastError: Error;

 for (let attempt = 0; attempt < finalConfig.maxAttempts; attempt++) {
 // Check total timeout
 const elapsed = Date.now() - startTime;
 if (elapsed >= finalConfig.totalTimeoutMs) {
 throw new RetryExhaustedError(
 `Total timeout exceeded after ${elapsed}ms`,
 attempt,
 lastError
 );
 }

 try {
 // Execute with per-attempt timeout
 const result = await Promise.race([
 operation(),
 rejectAfterTimeout(finalConfig.timeoutMs)
 ]);
 
 // Success - log if it took retries
 if (attempt > 0) {
 logger.info('Operation succeeded after retry', {
 attempts: attempt + 1,
 totalTime: Date.now() - startTime
 });
 }
 
 return result;
 
 } catch (error) {
 lastError = error as Error;
 
 // Log the failure
 logger.warn('Operation failed', {
 attempt: attempt + 1,
 maxAttempts: finalConfig.maxAttempts,
 error: error.message,
 willRetry: isRetryable(error) && attempt < finalConfig.maxAttempts - 1
 });

 // Don't retry non-retryable errors
 if (!isRetryable(error)) {
 throw error;
 }

 // Don't delay after last attempt
 if (attempt < finalConfig.maxAttempts - 1) {
 const delay = calculateDelay(attempt, finalConfig);
 logger.debug(`Waiting ${delay}ms before retry`);
 await setTimeout(delay);
 }
 }
 }

 // All retries exhausted
 throw new RetryExhaustedError(
 `Operation failed after ${finalConfig.maxAttempts} attempts`,
 finalConfig.maxAttempts,
 lastError
 );
}

// Timeout helper
function rejectAfterTimeout(ms: number): Promise<never> {
 return new Promise((_, reject) => {
 setTimeout(() => reject(new TimeoutError(`Operation timed out after ${ms}ms`)), ms);
 });
}

// Custom error for exhausted retries
class RetryExhaustedError extends Error {
 constructor(
 message: string,
 public attempts: number,
 public lastError: Error
 ) {
 super(message);
 this.name = 'RetryExhaustedError';
 }
}
```

### Usage Example

```typescript
// Payment processor call with retry
async function chargePayment(paymentData: PaymentInput): Promise<PaymentResult> {
 return withRetry(
 async () => {
 const response = await fetch('https://api.payment.com/charge', {
 method: 'POST',
 body: JSON.stringify(paymentData),
 headers: { 'Authorization': `Bearer ${API_KEY}` }
 });

 if (!response.ok) {
 const error = new ApiError(`Payment API error: ${response.status}`);
 error.statusCode = response.status;
 throw error;
 }

 return response.json();
 },
 {
 maxAttempts: 3,
 baseDelayMs: 1000,
 timeoutMs: 10000,
 totalTimeoutMs: 30000
 }
 );
}

// Usage in application
try {
 const result = await chargePayment({ amount: 1000, currency: 'USD' });
 console.log('Payment succeeded:', result);
} catch (error) {
 if (error instanceof RetryExhaustedError) {
 console.error('Payment failed after all retries:', error.lastError);
 } else {
 // Non-retryable error (4xx, validation)
 console.error('Payment failed (not retriable):', error);
 }
}
```

### Backoff Visualization

```
Attempt 1: Immediate
Attempt 2: ~1.0-1.5s delay (1s × 2^0 × jitter)
Attempt 3: ~2.0-3.0s delay (1s × 2^1 × jitter)
(Would be) Attempt 4: ~4.0-6.0s delay (1s × 2^2 × jitter)

Total max time: ~6-10s + operation time
```

When calling external services, APIs, or any operation with transient failures

  • Always add jitter to prevent thundering herd when many clients retry simultaneously
  • Make retryable vs. non-retryable explicit, retrying 400 Bad Request is pointless

4. Error Boundary Design

Design error boundaries for this application:

**Application structure:**
```
[DESCRIBE OR DIAGRAM APP STRUCTURE]
```

**Framework:** [REACT / VUE / ANGULAR / BACKEND SERVICE]

**Error sources:**
- [SOURCE 1 - e.g. API calls]
- [SOURCE 2 - e.g. User input]
- [SOURCE 3 - e.g. Third-party scripts]

**Fallback requirements:**
- [WHAT TO SHOW WHEN ERRORS OCCUR]
- [WHAT FUNCTIONALITY TO PRESERVE]

**Provide:**
1. Error boundary placement strategy
2. Fallback UI components
3. Error reporting integration
4. Recovery mechanisms
Application: React dashboard with multiple independent widgets
Structure: Layout > Sidebar + MainContent > Multiple Widget components
Error sources: Each widget fetches its own data, some use third-party charts
Fallbacks: Failed widget shows error message, rest of dashboard stays functional
## Error Boundary Strategy

### Boundary Placement

```
┌─────────────────────────────────────────────────────────────┐
│ AppErrorBoundary (catches fatal errors, shows full-page) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Layout │ │
│ │ ┌─────────┐ ┌─────────────────────────────────────────┐ │ │
│ │ │Sidebar │ │ MainContent │ │ │
│ │ │(no │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │boundary │ │ │ WidgetGrid │ │ │ │
│ │ │- static)│ │ │ ┌───────────┐ ┌───────────┐ │ │ │ │
│ │ │ │ │ │ │WidgetError│ │WidgetError│ │ │ │ │
│ │ │ │ │ │ │ Boundary │ │ Boundary │ │ │ │ │
│ │ │ │ │ │ │┌─────────┐│ │┌─────────┐│ │ │ │ │
│ │ │ │ │ │ ││ Widget ││ ││ Widget ││ │ │ │ │
│ │ │ │ │ │ │└─────────┘│ │└─────────┘│ │ │ │ │
│ │ │ │ │ │ └───────────┘ └───────────┘ │ │ │ │
│ │ └─────────┘ │ └─────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```

### Implementation

```tsx
// 1. Reusable Error Boundary Component
import { Component, ErrorInfo, ReactNode } from 'react';
import * as Sentry from '@sentry/react';

interface Props {
 children: ReactNode;
 fallback?: ReactNode | ((error: Error, retry: () => void) => ReactNode);
 onError?: (error: Error, errorInfo: ErrorInfo) => void;
 level?: 'widget' | 'section' | 'app';
}

interface State {
 hasError: boolean;
 error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
 state: State = { hasError: false, error: null };

 static getDerivedStateFromError(error: Error): State {
 return { hasError: true, error };
 }

 componentDidCatch(error: Error, errorInfo: ErrorInfo) {
 // Report to error tracking
 Sentry.captureException(error, {
 contexts: {
 react: { componentStack: errorInfo.componentStack }
 },
 tags: { boundary_level: this.props.level || 'unknown' }
 });

 // Custom error handler
 this.props.onError?.(error, errorInfo);
 }

 retry = () => {
 this.setState({ hasError: false, error: null });
 };

 render() {
 if (this.state.hasError) {
 // Custom fallback or default
 if (typeof this.props.fallback === 'function') {
 return this.props.fallback(this.state.error!, this.retry);
 }
 if (this.props.fallback) {
 return this.props.fallback;
 }
 // Default fallback based on level
 return <DefaultFallback level={this.props.level} onRetry={this.retry} />;
 }

 return this.props.children;
 }
}

// 2. Level-specific fallbacks
function DefaultFallback({ level, onRetry }: { level?: string; onRetry: () => void }) {
 switch (level) {
 case 'widget':
 return (
 <div className="widget-error p-4 bg-gray-50 rounded-lg text-center">
 <AlertCircle className="mx-auto h-8 w-8 text-gray-400" />
 <p className="mt-2 text-sm text-gray-600">Unable to load this widget</p>
 <button
 onClick={onRetry}
 className="mt-2 text-sm text-blue-600 hover:underline"
 >
 Try again
 </button>
 </div>
 );
 case 'section':
 return (
 <div className="section-error p-8 text-center">
 <h3 className="text-lg font-medium">Something went wrong</h3>
 <p className="mt-1 text-gray-600">This section couldn't be loaded.</p>
 <button onClick={onRetry} className="mt-4 btn btn-secondary">
 Retry
 </button>
 </div>
 );
 case 'app':
 default:
 return (
 <div className="app-error min-h-screen flex items-center justify-center">
 <div className="text-center">
 <h1 className="text-2xl font-bold">Something went wrong</h1>
 <p className="mt-2 text-gray-600">
 We've been notified and are working on a fix.
 </p>
 <button
 onClick={() => window.location.reload()}
 className="mt-4 btn btn-primary"
 >
 Refresh page
 </button>
 </div>
 </div>
 );
 }
}
```

### Usage in Dashboard

```tsx
// App.tsx - Top level boundary
function App() {
 return (
 <ErrorBoundary level="app">
 <Dashboard />
 </ErrorBoundary>
 );
}

// Dashboard.tsx - Widget-level boundaries
function Dashboard() {
 const widgets = useWidgets();

 return (
 <Layout>
 <WidgetGrid>
 {widgets.map((widget) => (
 <ErrorBoundary
 key={widget.id}
 level="widget"
 fallback={(error, retry) => (
 <WidgetErrorCard
 widgetName={widget.name}
 error={error}
 onRetry={retry}
 />
 )}
 onError={(error) => {
 // Track which widget failed
 analytics.track('widget_error', {
 widget_id: widget.id,
 error: error.message
 });
 }}
 >
 <WidgetRenderer widget={widget} />
 </ErrorBoundary>
 ))}
 </WidgetGrid>
 </Layout>
 );
}

// Widget with data fetching error handling
function WidgetRenderer({ widget }: { widget: Widget }) {
 const { data, error, isLoading, refetch } = useQuery({
 queryKey: ['widget', widget.id],
 queryFn: () => fetchWidgetData(widget.id),
 retry: 2, // React Query handles data fetching retries
 });

 // Data loading errors (different from render errors)
 if (error) {
 return (
 <WidgetLoadError
 message="Failed to load data"
 onRetry={refetch}
 />
 );
 }

 if (isLoading) return <WidgetSkeleton />;

 // This may throw during render - caught by ErrorBoundary
 return <widget.component data={data} />;
}
```

### Error Recovery Patterns

| Error Type | Recovery Strategy |
|------------|-------------------|
| Data fetch failed | Show error in widget, retry button, other widgets unaffected |
| Render error | ErrorBoundary catches, shows fallback, retry resets component |
| Third-party script | Boundary isolates, widget shows placeholder |
| Fatal app error | Full-page error, suggest refresh |

When building UIs with independent components that shouldn't crash the entire application

  • Place boundaries at natural isolation points, if a widget failing shouldn't break the dashboard, it needs its own boundary
  • Distinguish data fetching errors (handle in component) from render errors (handle in boundary)

Common Mistakes to Avoid

Catching all exceptions generically, hides bugs and makes debugging difficult

Showing internal error details to users, security risk and poor UX

Not logging errors with enough context, makes production debugging impossible

Frequently Asked Questions

Error handling separates solid applications from fragile ones. These prompts help you design error handling strategies that catch failures gracefully, provide useful feedback to users and developers, and enable recovery when possible. The goal is code that fails predictably and informatively, not silently or catastrophically.

Related Templates

Have your own prompt to optimize?