Install any skill in seconds. Free to start, no credit card required.
Get Started Free →🛡️ AI-powered error handling assistant that transforms fragile code into resilient systems. Automatically generate comprehensive error handling, recovery strategies, and graceful degradation. Because every production system deserves bulletproof error handling.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 317% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 422% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 385% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 221% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 211% | 0% |
> Turn fragile code into production-ready, resilient systems.
An intelligent error handling assistant that automatically:
The harsh reality:
cuihua-error-handler fixes all of this.
Tell your OpenClaw agent: > "Check error handling coverage in src/"
The agent will:
> "Add error handling to getUserById in api/users.js"
The agent will:
> "Add circuit breaker to payment service"
The agent will:
Automatically finds missing error handling:
javascript// ❌ BEFORE - Fragile code async function getUserById(id) { const res = await fetch(`/api/users/${id}`); return res.json(); } // 🔍 DETECTED ISSUES: // - No error handling for network failures // - No handling for non-200 responses // - No handling for invalid JSON // - No logging for debugging
Generates production-ready error handling:
javascript// ✅ AFTER - Bulletproof code class UserServiceError extends Error { constructor(message, options = {}) { super(message); this.name = 'UserServiceError'; this.statusCode = options.statusCode; this.originalError = options.cause; } } async function getUserById(id) { try { // Validation if (!id || typeof id !== 'string') { throw new UserServiceError('Invalid user ID', { statusCode: 400 }); } // Network request with timeout const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); const res = await fetch(`/api/users/${id}`, { signal: controller.signal }); clearTimeout(timeout); // HTTP error handling if (!res.ok) { if (res.status === 404) { throw new UserServiceError(`User ${id} not found`, { statusCode: 404 }); } if (res.status >= 500) { throw new UserServiceError('Server error, please retry', { statusCode: 502 }); } throw new UserServiceError(`HTTP ${res.status}`, { statusCode: res.status }); } // JSON parsing with error handling let data; try { data = await res.json(); } catch (parseError) { throw new UserServiceError('Invalid response format', { statusCode: 502, cause: parseError }); } return data; } catch (error) { // Network errors (timeout, connection refused) if (error.name === 'AbortError') { logger.error('getUserById timeout', { id, timeout: 5000 }); throw new UserServiceError('Request timeout', { statusCode: 504, cause: error }); } if (error.message.includes('fetch failed')) { logger.error('getUserById network error', { id, error: error.message }); throw new UserServiceError('Network error', { statusCode: 503, cause: error }); } // Re-throw UserServiceError if (error instanceof UserServiceError) { logger.error('getUserById failed', { id, error: error.message }); throw error; } // Unexpected errors logger.error('getUserById unexpected error', { id, error }); throw new UserServiceError('Internal error', { statusCode: 500, cause: error }); } }
Smart retry with exponential backoff:
javascriptasync function retryWithBackoff(fn, options = {}) { const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000, backoffFactor = 2, shouldRetry = (error) => true } = options; let lastError; let delay = initialDelay; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn(); } catch (error) { lastError = error; // Check if we should retry if (attempt === maxRetries || !shouldRetry(error)) { throw error; } // Wait before retry logger.warn(`Retry attempt ${attempt + 1}/${maxRetries} after ${delay}ms`, { error: error.message }); await new Promise(resolve => setTimeout(resolve, delay)); // Exponential backoff delay = Math.min(delay * backoffFactor, maxDelay); } } throw lastError; } // Usage async function fetchUserWithRetry(id) { return retryWithBackoff( () => getUserById(id), { maxRetries: 3, shouldRetry: (error) => { // Retry on network errors and 5xx return error.statusCode >= 500 || error.name === 'NetworkError'; } } ); }
Prevent cascading failures:
javascriptclass CircuitBreaker { constructor(fn, options = {}) { this.fn = fn; this.failureThreshold = options.failureThreshold || 5; this.resetTimeout = options.resetTimeout || 60000; this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN this.failureCount = 0; this.nextAttempt = Date.now(); } async execute(...args) { if (this.state === 'OPEN') { if (Date.now() < this.nextAttempt) { throw new Error('Circuit breaker is OPEN'); } // Try to recover this.state = 'HALF_OPEN'; } try { const result = await this.fn(...args); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } onSuccess() { this.failureCount = 0; if (this.state === 'HALF_OPEN') { this.state = 'CLOSED'; logger.info('Circuit breaker recovered'); } } onFailure() { this.failureCount++; if (this.failureCount >= this.failureThreshold) { this.state = 'OPEN'; this.nextAttempt = Date.now() + this.resetTimeout; logger.error('Circuit breaker opened', { failureCount: this.failureCount, resetTimeout: this.resetTimeout }); } } } // Usage const getUserBreaker = new CircuitBreaker(getUserById, { failureThreshold: 5, resetTimeout: 60000 }); async function fetchUserSafely(id) { try { return await getUserBreaker.execute(id); } catch (error) { if (error.message === 'Circuit breaker is OPEN') { // Return cached data or default return getCachedUser(id) || { id, name: 'Unknown', error: true }; } throw error; } }
Fallback to cached/default data:
javascriptasync function getUserWithFallback(id) { try { // Try primary source return await getUserById(id); } catch (error) { logger.warn('Primary source failed, trying fallback', { id, error: error.message }); try { // Try cache const cached = await cache.get(`user:${id}`); if (cached) { logger.info('Returned cached user', { id }); return { ...cached, _cached: true }; } } catch (cacheError) { logger.error('Cache also failed', { id, error: cacheError.message }); } // Return default user logger.warn('Returning default user', { id }); return { id, name: 'Guest User', _default: true, _error: error.message }; } }
Comprehensive coverage reporting:
🛡️ Error Handling Coverage Report
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📁 Files analyzed: 42
🔍 Functions analyzed: 156
- Async functions: 89
- Sync functions: 67
📊 Overall coverage: 68%
❌ Missing error handling (28 functions):
Critical (needs immediate attention):
1. api/payment.js:45 - processPayment()
Risk: High - Financial transaction
Recommendation: Add try/catch + transaction rollback
2. api/auth.js:23 - verifyToken()
Risk: High - Security critical
Recommendation: Add error handling + logging
3. db/users.js:78 - updateUserProfile()
Risk: Medium - Data mutation
Recommendation: Add validation + rollback
⚠️ Weak error handling (12 functions):
1. api/users.js:12 - getUserById()
Issue: Generic catch block without specific handling
Recommendation: Add error type differentiation
2. services/email.js:34 - sendWelcomeEmail()
Issue: Swallowed errors (empty catch block)
Recommendation: Add logging + alerting
💡 Recommendations:
- Add error handling to 28 unprotected functions
- Improve 12 weak error handlers
- Target coverage: 90%+ for production readinessUser: "Add error handling to POST /api/orders"
Agent:
javascript// ✅ Generated comprehensive error handling app.post('/api/orders', async (req, res) => { try { // Validation const { userId, items, paymentMethod } = req.body; if (!userId || !items || !paymentMethod) { return res.status(400).json({ error: 'Missing required fields', required: ['userId', 'items', 'paymentMethod'] }); } if (!Array.isArray(items) || items.length === 0) { return res.status(400).json({ error: 'Items must be a non-empty array' }); } // Create order with error handling let order; try { order = await createOrder({ userId, items, paymentMethod }); } catch (error) { if (error.code === 'INSUFFICIENT_STOCK') { return res.status(409).json({ error: 'Some items are out of stock', unavailable: error.items }); } throw error; // Re-throw for outer handler } // Process payment with retry let payment; try { payment = await retryWithBackoff( () => processPayment(order.id, paymentMethod), { maxRetries: 3 } ); } catch (error) { // Rollback order await cancelOrder(order.id); if (error.code === 'PAYMENT_DECLINED') { return res.status(402).json({ error: 'Payment declined', reason: error.reason }); } throw error; } // Success res.status(201).json({ orderId: order.id, paymentId: payment.id, status: 'confirmed' }); } catch (error) { logger.error('Order creation failed', { body: req.body, error: error.message, stack: error.stack }); res.status(500).json({ error: 'Failed to create order', message: process.env.NODE_ENV === 'development' ? error.message : 'Please try again later' }); } });
User: "Add error handling to database queries"
Agent:
javascriptclass UserRepository { async findById(id) { try { const user = await db.query( 'SELECT * FROM users WHERE id = $1', [id] ); if (!user) { throw new NotFoundError(`User ${id} not found`); } return user; } catch (error) { if (error instanceof NotFoundError) { throw error; } logger.error('Database query failed', { method: 'findById', id, error: error.message }); throw new DatabaseError('Failed to fetch user', { cause: error }); } } async update(id, data) { const client = await db.getClient(); try { await client.query('BEGIN'); const result = await client.query( 'UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *', [data.name, data.email, id] ); if (result.rowCount === 0) { throw new NotFoundError(`User ${id} not found`); } await client.query('COMMIT'); return result.rows[0]; } catch (error) { await client.query('ROLLBACK'); if (error.code === '23505') { // Unique violation throw new ConflictError('Email already exists'); } if (error instanceof NotFoundError) { throw error; } logger.error('User update failed', { id, data, error: error.message }); throw new DatabaseError('Failed to update user', { cause: error }); } finally { client.release(); } } }
User: "Add resilience to third-party API calls"
Agent:
javascriptclass WeatherAPI { constructor() { this.breaker = new CircuitBreaker(this._fetch.bind(this), { failureThreshold: 5, resetTimeout: 60000 }); this.cache = new Map(); } async getWeather(city) { // Try cache first const cached = this.cache.get(city); if (cached && Date.now() - cached.timestamp < 300000) { // 5 min cache logger.info('Returning cached weather', { city }); return cached.data; } try { // Call with circuit breaker const data = await this.breaker.execute(city); // Update cache this.cache.set(city, { data, timestamp: Date.now() }); return data; } catch (error) { logger.error('Weather API failed', { city, error: error.message }); // Return stale cache if available if (cached) { logger.warn('Returning stale cached data', { city }); return { ...cached.data, _stale: true }; } // Return default return { city, temperature: null, condition: 'Unknown', _error: error.message }; } } async _fetch(city) { const response = await retryWithBackoff( () => fetch(`https://api.weather.com/v1/${city}`), { maxRetries: 3, shouldRetry: (error) => { // Don't retry client errors return !error.statusCode || error.statusCode >= 500; } } ); if (!response.ok) { throw new Error(`Weather API error: ${response.status}`); } return response.json(); } }
Create .errorhandlerrc.json:
json{ "coverage": { "minimum": 80, "target": 95, "failOnBelow": true }, "patterns": { "enableRetry": true, "enableCircuitBreaker": true, "enableFallback": true, "maxRetries": 3, "retryDelay": 1000 }, "logging": { "logLevel": "error", "includeStack": true, "structuredLogging": true }, "customErrors": { "baseClass": "AppError", "errorTypes": [ "ValidationError", "NotFoundError", "UnauthorizedError", "ForbiddenError" ] } }
javascript// Base error class class AppError extends Error { constructor(message, options = {}) { super(message); this.name = this.constructor.name; this.statusCode = options.statusCode || 500; this.code = options.code; this.originalError = options.cause; } } // Domain-specific errors class ValidationError extends AppError { constructor(message, fields) { super(message, { statusCode: 400 }); this.fields = fields; } } class NotFoundError extends AppError { constructor(resource) { super(`${resource} not found`, { statusCode: 404 }); this.resource = resource; } } class UnauthorizedError extends AppError { constructor(message = 'Unauthorized') { super(message, { statusCode: 401 }); } } class ConflictError extends AppError { constructor(message) { super(message, { statusCode: 409 }); } } class ServiceUnavailableError extends AppError { constructor(service) { super(`${service} is temporarily unavailable`, { statusCode: 503 }); this.service = service; } }
MIT License - see LICENSE for details.
Built with 🌸 by 翠花 (Cuihua) for the OpenClaw community.
Because production systems deserve bulletproof error handling.
Made with 🌸 | Cuihua Series | ClawHub Pioneer
_Transform fragile code into resilient systems._
Other measured skills in the registry, with their headline benchmark lift.