#!/usr/bin/env node /** * Advanced Error Handling Example * * This example demonstrates the SDK's typed error handling system, * showing how to catch and handle specific error types gracefully. * * Use cases: * - Implementing retry logic for rate limits * - Handling permission errors with user prompts * - Building robust error recovery systems */ import { claude, detectErrorType, createTypedError, isRateLimitError, isToolPermissionError, isAuthenticationError, isNetworkError, isTimeoutError, isValidationError } from '@instantlyeasy/claude-code-sdk-ts'; async function errorHandlingExample() { console.log('šŸ›”ļø Advanced Error Handling Example\n'); // Example 1: Handling specific error types console.log('1. Handling Specific Error Types'); console.log('--------------------------------\n'); // Test different error scenarios const errorScenarios = [ { name: 'Tool Permission Error', setup: () => claude().denyTools('Bash'), prompt: 'Run the command: ls -la' }, { name: 'Model Validation Error', setup: () => claude().withModel('invalid-model'), prompt: 'Hello' } ]; for (const scenario of errorScenarios) { console.log(`Testing ${scenario.name}:`); try { await scenario.setup() .query(scenario.prompt) .asText(); console.log('āœ… No error occurred'); } catch (error) { const errorType = detectErrorType(error.message); console.log(`āŒ Caught error type: ${errorType}`); console.log(` Message: ${error.message}`); // Type-specific handling if (error.message.includes('denied') || error.message.includes('permission')) { console.log(' → This looks like a permission issue'); } else if (error.message.includes('model')) { console.log(' → This looks like a model configuration issue'); } } console.log(); } // Example 2: Simulating error types console.log('\n2. Simulating Different Error Types'); console.log('-----------------------------------\n'); // Create typed errors for demonstration const simulatedErrors = [ createTypedError('rate_limit_error', 'Too many requests', { retryAfter: 30 }), createTypedError('tool_permission_error', 'Bash tool denied', { tool: 'Bash' }), createTypedError('authentication_error', 'Authentication failed - run: claude login'), createTypedError('network_error', 'Connection timeout'), createTypedError('timeout_error', 'Query timeout', { timeout: 5000 }), createTypedError('validation_error', 'Invalid model name', { field: 'model' }) ]; for (const error of simulatedErrors) { console.log(`${error.constructor.name}:`); console.log(` Message: ${error.message}`); console.log(` Type detection: ${detectErrorType(error.message)}`); // Show error-specific properties if (isRateLimitError(error)) { console.log(` Retry after: ${error.retryAfter}s`); } else if (isToolPermissionError(error)) { console.log(` Tool: ${error.tool}`); } else if (isTimeoutError(error)) { console.log(` Timeout: ${error.timeout}ms`); } else if (isValidationError(error)) { console.log(` Field: ${error.field}`); } console.log(); } // Example 3: Retry logic demonstration console.log('\n3. Retry Logic for Transient Errors'); console.log('-----------------------------------\n'); async function queryWithRetry(prompt, maxRetries = 3) { let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { console.log(`Attempt ${attempt}/${maxRetries}...`); // Simulate occasional failures if (attempt < 2 && Math.random() < 0.7) { throw createTypedError('network_error', 'Simulated network error'); } return await claude() .withModel('sonnet') .withTimeout(10000) .query(prompt) .asText(); } catch (error) { lastError = error; console.log(`āŒ Attempt ${attempt} failed: ${error.message}`); if (attempt < maxRetries) { if (isRateLimitError(error)) { const waitTime = error.retryAfter || Math.pow(2, attempt); console.log(`ā³ Rate limited. Waiting ${waitTime}s...`); await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); } else if (isNetworkError(error)) { const waitTime = Math.pow(2, attempt - 1); console.log(`ā³ Network error. Waiting ${waitTime}s...`); await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); } else { // Non-retryable error throw error; } } } } throw lastError; } try { const result = await queryWithRetry('Say "Hello from retry logic!"'); console.log('āœ… Success:', result); } catch (error) { console.error('āŒ Failed after all retries:', error.message); } // Example 4: Graceful degradation console.log('\n\n4. Graceful Degradation'); console.log('-----------------------\n'); async function queryWithFallback(prompt) { const strategies = [ { name: 'Primary', model: 'opus', timeout: 5000 }, { name: 'Secondary', model: 'sonnet', timeout: 10000 }, { name: 'Fallback', model: 'sonnet', timeout: 15000 } ]; for (const strategy of strategies) { try { console.log(`Trying ${strategy.name} strategy (${strategy.model})...`); return await claude() .withModel(strategy.model) .withTimeout(strategy.timeout) .query(prompt) .asText(); } catch (error) { console.warn(`āš ļø ${strategy.name} failed: ${error.message}`); if (strategy === strategies[strategies.length - 1]) { throw error; // Last strategy failed } } } } try { const result = await queryWithFallback('What is 2+2?'); console.log('āœ… Success with fallback:', result); } catch (error) { console.error('āŒ All strategies failed:', error.message); } // Example 5: Error recovery patterns console.log('\n\n5. Error Recovery Patterns'); console.log('--------------------------\n'); class ErrorRecovery { static async withCircuitBreaker(fn, options = {}) { const { threshold = 3, resetTime = 30000 } = options; const state = { failures: 0, lastFailure: null, isOpen: false }; return async (...args) => { // Check if circuit is open if (state.isOpen) { const timeSinceFailure = Date.now() - state.lastFailure; if (timeSinceFailure < resetTime) { throw new Error('Circuit breaker is OPEN'); } // Try to close circuit state.isOpen = false; console.log('šŸ”„ Circuit breaker: Attempting to close...'); } try { const result = await fn(...args); // Success - reset failure count if (state.failures > 0) { console.log('āœ… Circuit breaker: Reset after success'); state.failures = 0; } return result; } catch (error) { state.failures++; state.lastFailure = Date.now(); if (state.failures >= threshold) { state.isOpen = true; console.log(`🚫 Circuit breaker: OPEN after ${state.failures} failures`); } throw error; } }; } static async withTimeout(fn, timeout) { return Promise.race([ fn(), new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${timeout}ms`)), timeout) ) ]); } static async withFallbackValue(fn, fallbackValue) { try { return await fn(); } catch (error) { console.log(`āš ļø Using fallback value due to: ${error.message}`); return fallbackValue; } } } // Test circuit breaker const protectedQuery = ErrorRecovery.withCircuitBreaker( async (prompt) => { // Simulate some failures if (Math.random() < 0.4) { throw new Error('Simulated service error'); } return `Response: ${prompt}`; }, { threshold: 2, resetTime: 5000 } ); console.log('Testing circuit breaker:'); for (let i = 0; i < 5; i++) { try { const result = await protectedQuery('Test query'); console.log(` Attempt ${i + 1}: ${result}`); } catch (error) { console.log(` Attempt ${i + 1}: āŒ ${error.message}`); } await new Promise(resolve => setTimeout(resolve, 1000)); } // Example 6: Error context and logging console.log('\n\n6. Error Context and Logging'); console.log('----------------------------\n'); class ErrorLogger { static log(error, context = {}) { const errorInfo = { timestamp: new Date().toISOString(), type: detectErrorType(error.message), message: error.message, context, stack: error.stack?.split('\n').slice(0, 3).join('\n') }; console.log('šŸ“‹ Error Log Entry:'); console.log(JSON.stringify(errorInfo, null, 2)); // Return user-friendly message const userMessages = { authentication_error: 'Authentication required. Please run: claude login', rate_limit_error: 'Too many requests. Please try again later.', network_error: 'Connection issue. Please check your internet.', tool_permission_error: 'This operation requires additional permissions.', timeout_error: 'The operation took too long. Please try again.', validation_error: 'Invalid input. Please check your parameters.', api_error: 'Something went wrong. Please try again.' }; return userMessages[errorInfo.type] || userMessages.api_error; } } // Test error logging const testError = createTypedError('authentication_error', 'Authentication failed'); const userMessage = ErrorLogger.log(testError, { userId: 'user123', action: 'query', model: 'opus' }); console.log('\nšŸ‘¤ User-friendly message:', userMessage); console.log('\n✨ Error handling examples completed!'); } // Error handling wrapper with final fallback errorHandlingExample().catch(error => { console.error('\nšŸ’„ Unhandled error in example:', error); console.error('This is the final error boundary.'); process.exit(1); });