Agent skill
error-handling-patterns
Robust error handling patterns for TypeScript applications
Install this agent skill to your Project
npx add-skill https://github.com/autohandai/community-skills/tree/main/error-handling-patterns
SKILL.md
Error Handling Patterns
Core Principles
- Fail fast - Detect and report errors early
- Fail safely - Errors shouldn't crash the application
- Fail informatively - Provide actionable error messages
- Recover gracefully - Handle expected failures
Custom Error Classes
// Base application error
export class AppError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 500,
public readonly details?: unknown
) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
toJSON() {
return {
code: this.code,
message: this.message,
...(this.details && { details: this.details }),
};
}
}
// Specific error types
export class ValidationError extends AppError {
constructor(message: string, details?: Record<string, string>) {
super(message, 'VALIDATION_ERROR', 400, details);
}
}
export class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(`${resource} with id '${id}' not found`, 'NOT_FOUND', 404);
}
}
export class UnauthorizedError extends AppError {
constructor(message = 'Authentication required') {
super(message, 'UNAUTHORIZED', 401);
}
}
export class ForbiddenError extends AppError {
constructor(message = 'Insufficient permissions') {
super(message, 'FORBIDDEN', 403);
}
}
Result Type Pattern
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
// Helper functions
function ok<T>(data: T): Result<T, never> {
return { success: true, data };
}
function err<E>(error: E): Result<never, E> {
return { success: false, error };
}
// Usage
async function findUser(id: string): Promise<Result<User, NotFoundError>> {
const user = await db.users.findUnique({ where: { id } });
if (!user) {
return err(new NotFoundError('User', id));
}
return ok(user);
}
// Consuming
const result = await findUser('123');
if (!result.success) {
console.error(result.error.message);
return;
}
const user = result.data; // Type-safe User
Error Boundaries (React)
import { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, info: ErrorInfo) => void;
}
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, info: ErrorInfo) {
this.props.onError?.(error, info);
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? (
<div role="alert">
<h2>Something went wrong</h2>
<pre>{this.state.error?.message}</pre>
</div>
);
}
return this.props.children;
}
}
Async Error Handling
// Wrapper for async functions
function tryCatch<T>(
promise: Promise<T>
): Promise<[null, T] | [Error, null]> {
return promise
.then((data) => [null, data] as [null, T])
.catch((error) => [error as Error, null]);
}
// Usage
const [error, user] = await tryCatch(fetchUser(id));
if (error) {
handleError(error);
return;
}
// user is guaranteed to be defined here
// Multiple operations
async function processOrder(orderId: string) {
const [orderError, order] = await tryCatch(getOrder(orderId));
if (orderError) return err(orderError);
const [paymentError] = await tryCatch(processPayment(order));
if (paymentError) {
await tryCatch(rollbackOrder(order));
return err(paymentError);
}
return ok(order);
}
Retry Pattern
interface RetryOptions {
maxAttempts: number;
delayMs: number;
backoff?: 'linear' | 'exponential';
shouldRetry?: (error: Error) => boolean;
}
async function withRetry<T>(
fn: () => Promise<T>,
options: RetryOptions
): Promise<T> {
const { maxAttempts, delayMs, backoff = 'exponential', shouldRetry } = options;
let lastError: Error;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (shouldRetry && !shouldRetry(lastError)) {
throw lastError;
}
if (attempt < maxAttempts) {
const delay = backoff === 'exponential'
? delayMs * Math.pow(2, attempt - 1)
: delayMs * attempt;
await sleep(delay);
}
}
}
throw lastError!;
}
// Usage
const data = await withRetry(() => fetchFromAPI(), {
maxAttempts: 3,
delayMs: 1000,
shouldRetry: (err) => err.message.includes('timeout'),
});
Validation Errors
import { z } from 'zod';
function validateInput<T>(schema: z.ZodSchema<T>, input: unknown): Result<T, ValidationError> {
const result = schema.safeParse(input);
if (!result.success) {
const details = result.error.flatten().fieldErrors;
return err(new ValidationError('Invalid input', details));
}
return ok(result.data);
}
// Usage
const userSchema = z.object({
email: z.string().email(),
age: z.number().min(18),
});
const result = validateInput(userSchema, req.body);
if (!result.success) {
return res.status(400).json(result.error.toJSON());
}
Logging Errors
interface ErrorContext {
userId?: string;
requestId?: string;
path?: string;
[key: string]: unknown;
}
function logError(error: Error, context: ErrorContext = {}) {
const payload = {
timestamp: new Date().toISOString(),
name: error.name,
message: error.message,
stack: error.stack,
...(error instanceof AppError && { code: error.code }),
...context,
};
console.error(JSON.stringify(payload));
// Send to error tracking service
if (process.env.NODE_ENV === 'production') {
// Sentry.captureException(error, { extra: context });
}
}
Best Practices
- Create specific error types for different failure modes
- Use Result types for expected failures (validation, not found)
- Throw errors for unexpected failures (bugs, system errors)
- Include context in error messages for debugging
- Log at boundaries - API handlers, event processors
- Validate at boundaries - external input, API responses
- Implement retry for transient failures
- Use error boundaries in React for graceful degradation
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
mapping-mitre-attack-techniques
Maps observed adversary behaviors, security alerts, and detection rules to MITRE ATT&CK techniques and sub-techniques to quantify detection coverage and guide control prioritization. Use when building an ATT&CK-based coverage heatmap, tagging SIEM alerts with technique IDs, aligning security controls to adversary playbooks, or reporting threat exposure to executives. Activates for requests involving ATT&CK Navigator, Sigma rules, MITRE D3FEND, or coverage gap analysis.
hunting-for-spearphishing-indicators
Hunt for spearphishing campaign indicators across email logs, endpoint telemetry, and network data to detect targeted email attacks.
analyzing-malicious-url-with-urlscan
URLScan.io is a free service for scanning and analyzing suspicious URLs. It captures screenshots, DOM content, HTTP transactions, JavaScript behavior, and network connections of web pages in an isolat
implementing-zero-standing-privilege-with-cyberark
Deploy CyberArk Secure Cloud Access to eliminate standing privileges in hybrid and multi-cloud environments using just-in-time access with time, entitlement, and approval controls.
implementing-pam-for-database-access
Deploy privileged access management for database systems including Oracle, SQL Server, PostgreSQL, and MySQL. Covers session proxy configuration, credential vaulting, query auditing, dynamic credentia
detecting-t1003-credential-dumping-with-edr
Detect OS credential dumping techniques targeting LSASS memory, SAM database, NTDS.dit, and cached credentials using EDR telemetry, Sysmon process access monitoring, and Windows security event correlation.
Didn't find tool you were looking for?