templates/skills/error-handling/SKILL.md
# Error Handling Patterns Comprehensive error handling for robust applications. > **Template Usage:** Customize for your framework (React, Vue, Express) and error tracking service (Sentry, Bugsnag, etc.). ## Error Class Hierarchy ```typescript // lib/errors.ts // Base application error export class AppError extends Error { constructor( message: string, public code: string, public statusCode: number = 500, public isOperational: boolean = true ) { super(message); t
npx skillsauth add javeedishaq/ai-workflow-orchestrator templates/skills/error-handlingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive error handling for robust applications.
Template Usage: Customize for your framework (React, Vue, Express) and error tracking service (Sentry, Bugsnag, etc.).
// lib/errors.ts
// Base application error
export 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
export class ValidationError extends AppError {
constructor(message: string, public fields?: Record<string, string[]>) {
super(message, 'VALIDATION_ERROR', 400);
}
}
export class NotFoundError extends AppError {
constructor(resource: string, id?: string) {
super(
id ? `${resource} with id ${id} not found` : `${resource} not found`,
'NOT_FOUND',
404
);
}
}
export class UnauthorizedError extends AppError {
constructor(message: string = 'Unauthorized') {
super(message, 'UNAUTHORIZED', 401);
}
}
export class ForbiddenError extends AppError {
constructor(message: string = 'Forbidden') {
super(message, 'FORBIDDEN', 403);
}
}
export class ConflictError extends AppError {
constructor(message: string) {
super(message, 'CONFLICT', 409);
}
}
export class RateLimitError extends AppError {
constructor(retryAfter?: number) {
super('Too many requests', 'RATE_LIMIT', 429);
}
}
// components/error-boundary.tsx
'use client';
import { Component, ReactNode } from 'react';
import * as Sentry from '@sentry/nextjs'; // or your error tracker
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}
interface State {
hasError: boolean;
error?: Error;
}
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Log to error tracking service
Sentry.captureException(error, {
extra: { componentStack: errorInfo.componentStack }
});
// Call custom handler
this.props.onError?.(error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || <DefaultErrorFallback error={this.state.error} />;
}
return this.props.children;
}
}
function DefaultErrorFallback({ error }: { error?: Error }) {
return (
<div role="alert" className="p-6 rounded-lg border border-destructive bg-destructive/10">
<h2 className="text-lg font-semibold text-destructive">Something went wrong</h2>
<p className="mt-2 text-sm text-muted-foreground">
We've been notified and are working on a fix.
</p>
{process.env.NODE_ENV === 'development' && error && (
<pre className="mt-4 p-2 bg-muted rounded text-xs overflow-auto">
{error.message}
</pre>
)}
<button
onClick={() => window.location.reload()}
className="mt-4 px-4 py-2 bg-primary text-primary-foreground rounded"
>
Reload page
</button>
</div>
);
}
// Usage
function App() {
return (
<ErrorBoundary fallback={<ErrorPage />}>
<MainContent />
</ErrorBoundary>
);
}
// app/error.tsx - Error boundary for route segment
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log to error tracking
Sentry.captureException(error);
}, [error]);
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h2 className="text-2xl font-bold">Something went wrong!</h2>
<button
onClick={reset}
className="mt-4 px-4 py-2 bg-primary text-white rounded"
>
Try again
</button>
</div>
);
}
// app/global-error.tsx - Root error boundary
'use client';
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</body>
</html>
);
}
// app/not-found.tsx - 404 page
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
</div>
);
}
// Server action error handling
'use server';
import { AppError, ValidationError } from '@/lib/errors';
export async function createItem(formData: FormData) {
try {
const data = parseFormData(formData);
// Validation
const validated = schema.safeParse(data);
if (!validated.success) {
return {
success: false,
error: 'Validation failed',
fields: validated.error.flatten().fieldErrors,
};
}
// Business logic
const result = await db.item.create({ data: validated.data });
return { success: true, data: result };
} catch (error) {
// Log error with context
console.error('createItem failed:', {
error,
formData: Object.fromEntries(formData),
});
// Return user-friendly error
if (error instanceof AppError) {
return {
success: false,
error: error.message,
code: error.code,
};
}
return {
success: false,
error: 'An unexpected error occurred',
};
}
}
// Express/API route error handling
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
// Log error
console.error('API Error:', {
error: err,
path: req.path,
method: req.method,
body: req.body,
user: req.user?.id,
});
// Send to error tracking
Sentry.captureException(err);
// Respond based on error type
if (err instanceof AppError) {
return res.status(err.statusCode).json({
success: false,
error: err.message,
code: err.code,
...(err instanceof ValidationError && { fields: err.fields }),
});
}
// Don't leak internal errors
return res.status(500).json({
success: false,
error: 'Internal server error',
});
});
// Wrap async functions for consistent error handling
export function withErrorHandling<T extends any[], R>(
fn: (...args: T) => Promise<R>,
options?: {
onError?: (error: Error) => void;
fallback?: R;
}
) {
return async (...args: T): Promise<R> => {
try {
return await fn(...args);
} catch (error) {
options?.onError?.(error as Error);
if (options?.fallback !== undefined) {
return options.fallback;
}
throw error;
}
};
}
// React Query error handling
const { data, error, isError } = useQuery({
queryKey: ['user', id],
queryFn: () => fetchUser(id),
retry: (failureCount, error) => {
// Don't retry on 4xx errors
if (error instanceof AppError && error.statusCode < 500) {
return false;
}
return failureCount < 3;
},
onError: (error) => {
toast.error(getUserFriendlyMessage(error));
},
});
// lib/error-messages.ts
const ERROR_MESSAGES: Record<string, string> = {
// Auth errors
UNAUTHORIZED: 'Please log in to continue',
FORBIDDEN: 'You don\'t have permission to do this',
SESSION_EXPIRED: 'Your session has expired. Please log in again.',
// Validation errors
VALIDATION_ERROR: 'Please check your input and try again',
INVALID_EMAIL: 'Please enter a valid email address',
PASSWORD_TOO_WEAK: 'Password must be at least 8 characters',
// Resource errors
NOT_FOUND: 'The requested item could not be found',
ALREADY_EXISTS: 'This item already exists',
CONFLICT: 'This action conflicts with existing data',
// Network/server errors
NETWORK_ERROR: 'Unable to connect. Please check your internet connection.',
TIMEOUT: 'The request took too long. Please try again.',
RATE_LIMIT: 'Too many requests. Please wait a moment and try again.',
SERVER_ERROR: 'Something went wrong on our end. We\'ve been notified.',
// Default
UNKNOWN: 'An unexpected error occurred. Please try again.',
};
export function getUserFriendlyMessage(error: unknown): string {
if (error instanceof AppError) {
return ERROR_MESSAGES[error.code] || error.message;
}
if (error instanceof Error) {
// Check for network errors
if (error.message.includes('fetch') || error.message.includes('network')) {
return ERROR_MESSAGES.NETWORK_ERROR;
}
}
return ERROR_MESSAGES.UNKNOWN;
}
// lib/logger.ts
import * as Sentry from '@sentry/nextjs';
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
interface LogContext {
userId?: string;
requestId?: string;
[key: string]: unknown;
}
class Logger {
constructor(private service: string) {}
private log(level: LogLevel, message: string, context?: LogContext) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
level,
service: this.service,
message,
...context,
};
// Console output (structured for production log aggregation)
console[level](JSON.stringify(logEntry));
// Send errors to tracking service
if (level === 'error' && context?.error) {
Sentry.captureException(context.error, {
extra: context,
tags: { service: this.service },
});
}
}
debug(message: string, context?: LogContext) {
if (process.env.NODE_ENV === 'development') {
this.log('debug', message, context);
}
}
info(message: string, context?: LogContext) {
this.log('info', message, context);
}
warn(message: string, context?: LogContext) {
this.log('warn', message, context);
}
error(message: string, context?: LogContext) {
this.log('error', message, context);
}
}
export const createLogger = (service: string) => new Logger(service);
// Usage
const logger = createLogger('UserService');
logger.error('Failed to create user', {
error: err,
userId: user.id,
email: input.email,
});
// lib/retry.ts
interface RetryOptions {
maxAttempts?: number;
delayMs?: number;
backoff?: 'linear' | 'exponential';
shouldRetry?: (error: Error) => boolean;
}
export async function withRetry<T>(
fn: () => Promise<T>,
options: RetryOptions = {}
): Promise<T> {
const {
maxAttempts = 3,
delayMs = 1000,
backoff = 'exponential',
shouldRetry = () => true,
} = options;
let lastError: Error;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (attempt === maxAttempts || !shouldRetry(lastError)) {
throw lastError;
}
const delay = backoff === 'exponential'
? delayMs * Math.pow(2, attempt - 1)
: delayMs * attempt;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError!;
}
// Usage
const data = await withRetry(
() => fetchExternalAPI(),
{
maxAttempts: 3,
delayMs: 1000,
backoff: 'exponential',
shouldRetry: (error) => {
// Only retry on network/server errors
if (error instanceof AppError) {
return error.statusCode >= 500;
}
return true;
},
}
);
tools
# Test Patterns Testing patterns for reliable, maintainable, and fast tests. > **Template Usage:** Customize for your test framework (Vitest, Jest, Playwright, etc.) and assertion library. ## Test Structure ```typescript // user.test.ts import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { userService } from '@/services/user.service'; import { createTestUser, cleanupTestData } from '@/tests/helpers'; describe('UserService', () => { let testUserId: string; befor
tools
# State Management Patterns Client-side state management patterns for modern applications. > **Template Usage:** Customize for your state library (React Query, Zustand, Jotai, Redux, etc.). ## State Categories | Type | Description | Solution | |------|-------------|----------| | **Server State** | Data from API/database | React Query, SWR | | **Client State** | UI state, user preferences | Zustand, Jotai, useState | | **Form State** | Form inputs, validation | React Hook Form, Formik | | **U
development
# Service Patterns Service layer patterns for clean architecture with proper error handling, logging, and type safety. > **Template Usage:** Customize for your ORM (Prisma, Drizzle, TypeORM, etc.) and logging solution. ## Result Type Pattern Never throw exceptions from services. Always return a Result type. ```typescript // lib/result.ts export type Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E }; export function ok<T>(data: T): Result<T, never> { r
testing
# Row-Level Security Patterns Database security patterns for multi-tenant and user-scoped data. > **Template Usage:** Customize for your database (PostgreSQL, Supabase, etc.) and auth system. ## RLS Fundamentals ### Enable RLS on Tables ```sql -- Enable RLS (required before policies take effect) ALTER TABLE users ENABLE ROW LEVEL SECURITY; ALTER TABLE posts ENABLE ROW LEVEL SECURITY; ALTER TABLE comments ENABLE ROW LEVEL SECURITY; -- Force RLS for table owners too (recommended) ALTER TABLE