Error handling patterns including exceptions, Result pattern, validation strategies, retry logic, and circuit breakers. **ALWAYS use when implementing error handling in backend code, APIs, use cases, or validation logic.** Use proactively for robust error handling, recovery mechanisms, and failure scenarios. Examples - "handle errors", "Result pattern", "throw exception", "validate input", "error recovery", "retry logic", "circuit breaker", "exception hierarchy".
Implements robust error handling patterns including exceptions, Result pattern, validation, retry logic, and circuit breakers.
/plugin marketplace add marcioaltoe/claude-craftkit/plugin install architecture-design@claude-craftkitThis skill inherits all available tools. When active, it can use any tool Claude has access to.
You are an expert in error handling patterns and strategies. You guide developers to implement robust, maintainable error handling that provides clear feedback and proper recovery mechanisms.
For complete backend implementation examples using these error handling patterns (Clean Architecture layers, DI Container, Use Cases, Repositories), see backend-engineer skill
You should proactively assist when:
// ❌ BAD: Base error class creates coupling
export abstract class DomainError extends Error {
// Forces all contexts to use same error structure
}
// ✅ GOOD: Each context has its own errors
// contexts/auth/domain/errors/auth-validation.error.ts
export class AuthValidationError extends Error {
constructor(message: string, public readonly field?: string) {
super(message);
this.name = "AuthValidationError";
}
}
// contexts/tax/domain/errors/tax-calculation.error.ts
export class TaxCalculationError extends Error {
constructor(message: string, public readonly ncmCode?: string) {
super(message);
this.name = "TaxCalculationError";
}
}
// ✅ Good - Use exceptions with context
export class CreateUserUseCase {
async execute(dto: CreateUserDto): Promise<User> {
if (!this.isValidEmail(dto.email)) {
throw new ValidationError("Invalid email format", {
email: dto.email,
field: "email",
});
}
try {
return await this.repository.save(user);
} catch (error) {
throw new DatabaseError("Failed to create user", {
originalError: error,
userId: user.id,
});
}
}
}
// ❌ Bad - Return codes
export class CreateUserUseCase {
async execute(dto: CreateUserDto): Promise<{
success: boolean;
user?: User;
error?: string;
}> {
if (!this.isValidEmail(dto.email)) {
return { success: false, error: "Invalid email" };
}
// Forces caller to check success flag everywhere
}
}
// ✅ Good - Explicit optional with undefined
export class UserService {
async findById(id: string): Promise<User | undefined> {
return this.repository.findById(id);
}
// Or throw if must exist
async getUserById(id: string): Promise<User> {
const user = await this.repository.findById(id);
if (!user) {
throw new NotFoundError(`User ${id} not found`);
}
return user;
}
}
// ❌ Bad - Returning null loses error context
export class UserService {
async findById(id: string): Promise<User | null> {
// Why null? Not found? Database error? Network error?
return null;
}
}
// ✅ Good - Rich error context
export class ValidationError extends Error {
constructor(
message: string,
public readonly context: Record<string, unknown>
) {
super(message);
this.name = "ValidationError";
}
}
throw new ValidationError("Invalid email format", {
email: dto.email,
field: "email",
rule: "email-format",
attemptedAt: new Date().toISOString(),
});
// ❌ Bad - No context
throw new Error("Invalid");
// Base domain exception
export abstract class DomainError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly context?: Record<string, unknown>
) {
super(message);
this.name = this.constructor.name;
}
}
// Specific domain exceptions
export class UserAlreadyExistsError extends DomainError {
constructor(email: string) {
super(`User with email ${email} already exists`, "USER_ALREADY_EXISTS", {
email,
});
}
}
export class InvalidPasswordError extends DomainError {
constructor(reason: string) {
super("Password does not meet requirements", "INVALID_PASSWORD", {
reason,
});
}
}
export class InsufficientPermissionsError extends DomainError {
constructor(userId: string, resource: string, action: string) {
super(
`User ${userId} cannot ${action} ${resource}`,
"INSUFFICIENT_PERMISSIONS",
{ userId, resource, action }
);
}
}
export class DatabaseError extends Error {
constructor(
message: string,
public readonly originalError: unknown,
public readonly query?: string
) {
super(message);
this.name = "DatabaseError";
}
}
export class ExternalServiceError extends Error {
constructor(
public readonly service: string,
message: string,
public readonly statusCode?: number,
public readonly originalError?: unknown
) {
super(`${service}: ${message}`);
this.name = "ExternalServiceError";
}
}
For operations with expected failures:
export type Result<T, E = Error> =
| { success: true; value: T }
| { success: false; error: E };
export class UserService {
async findByEmail(email: string): Promise<Result<User, NotFoundError>> {
const user = await this.repository.findByEmail(email);
if (!user) {
return {
success: false,
error: new NotFoundError("User not found"),
};
}
return { success: true, value: user };
}
}
// Usage
const result = await userService.findByEmail("user@example.com");
if (!result.success) {
console.error("User not found:", result.error.message);
return;
}
// TypeScript knows result.value is User here
const user = result.value;
Use Result for:
Use Exceptions for:
import { z } from "zod";
// ✅ Good - Validate at system boundaries
const CreateUserSchema = z.object({
email: z.string().email("Invalid email format"),
password: z
.string()
.min(8, "Password must be at least 8 characters")
.regex(/[A-Z]/, "Password must contain uppercase letter")
.regex(/[0-9]/, "Password must contain number"),
name: z
.string()
.min(2, "Name must be at least 2 characters")
.max(100, "Name must be at most 100 characters"),
age: z
.number()
.int("Age must be an integer")
.min(18, "Must be at least 18 years old")
.optional(),
});
export type CreateUserDto = z.infer<typeof CreateUserSchema>;
// In Hono controller
import { zValidator } from "@hono/zod-validator";
app.post("/users", zValidator("json", CreateUserSchema), async (c) => {
const data = c.req.valid("json"); // Type-safe and validated
const user = await createUserUseCase.execute(data);
return c.json(user, 201);
});
// ✅ Good - Validate in domain entities
export class Email {
private constructor(private readonly value: string) {}
static create(value: string): Result<Email, ValidationError> {
if (!value) {
return {
success: false,
error: new ValidationError("Email is required", { field: "email" }),
};
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
return {
success: false,
error: new ValidationError("Invalid email format", {
email: value,
field: "email",
}),
};
}
return { success: true, value: new Email(value) };
}
toString(): string {
return this.value;
}
}
// Usage
const emailResult = Email.create(dto.email);
if (!emailResult.success) {
throw emailResult.error;
}
const email = emailResult.value;
export async function withRetry<T>(
operation: () => Promise<T>,
options: {
maxRetries: number;
delayMs: number;
shouldRetry?: (error: unknown) => boolean;
}
): Promise<T> {
const { maxRetries, delayMs, shouldRetry = () => true } = options;
let lastError: unknown;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (attempt === maxRetries || !shouldRetry(error)) {
break;
}
await new Promise((resolve) =>
setTimeout(resolve, delayMs * (attempt + 1))
);
}
}
throw lastError;
}
// Usage
const user = await withRetry(() => userRepository.findById(id), {
maxRetries: 3,
delayMs: 1000,
shouldRetry: (error) => error instanceof DatabaseError,
});
export class CircuitBreaker {
private failureCount = 0;
private lastFailureTime?: number;
private state: "CLOSED" | "OPEN" | "HALF_OPEN" = "CLOSED";
constructor(
private readonly failureThreshold: number,
private readonly resetTimeoutMs: number
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === "OPEN") {
if (Date.now() - (this.lastFailureTime || 0) > this.resetTimeoutMs) {
this.state = "HALF_OPEN";
} else {
throw new Error("Circuit breaker is OPEN");
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
this.failureCount = 0;
this.state = "CLOSED";
}
private onFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = "OPEN";
}
}
}
// Usage
const breaker = new CircuitBreaker(5, 60000); // 5 failures, 60s timeout
const data = await breaker.execute(() => externalService.fetchData());
export class ConfigService {
async get<T>(key: string, fallback: T): Promise<T> {
try {
const value = await this.redis.get(key);
return value ? JSON.parse(value) : fallback;
} catch (error) {
console.error(`Failed to get config ${key}:`, error);
return fallback;
}
}
}
// Usage
const maxRetries = await configService.get("maxRetries", 3);
export interface LogContext {
userId?: string;
requestId?: string;
operation?: string;
[key: string]: unknown;
}
export class Logger {
error(message: string, error: Error, context?: LogContext): void {
console.error({
level: "error",
message,
error: {
name: error.name,
message: error.message,
stack: error.stack,
},
context,
timestamp: new Date().toISOString(),
});
}
warn(message: string, context?: LogContext): void {
console.warn({
level: "warn",
message,
context,
timestamp: new Date().toISOString(),
});
}
}
// Usage
try {
await userRepository.save(user);
} catch (error) {
logger.error("Failed to save user", error as Error, {
userId: user.id,
operation: "createUser",
requestId: context.requestId,
});
throw new DatabaseError("Failed to save user", error);
}
import { Hono } from "hono";
import type { Context } from "hono";
const app = new Hono();
// Global error handler
app.onError((err, c) => {
logger.error("Unhandled error", err, {
path: c.req.path,
method: c.req.method,
});
if (err instanceof ValidationError) {
return c.json(
{
error: "Validation failed",
message: err.message,
context: err.context,
},
400
);
}
if (err instanceof NotFoundError) {
return c.json(
{
error: "Resource not found",
message: err.message,
},
404
);
}
if (err instanceof DomainError) {
return c.json(
{
error: err.code,
message: err.message,
context: err.context,
},
400
);
}
// Unknown error - don't leak details
return c.json(
{
error: "Internal server error",
message: "An unexpected error occurred",
},
500
);
});
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.