error-handling

Comprehensive error handling patterns for Splits Network services and apps

16 stars

Best use case

error-handling is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Comprehensive error handling patterns for Splits Network services and apps

Teams using error-handling should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/error-handling/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/error-handling/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/error-handling/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How error-handling Compares

Feature / Agenterror-handlingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Comprehensive error handling patterns for Splits Network services and apps

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Error Handling Skill

This skill provides guidance for consistent, user-friendly error handling across Splits Network.

## Purpose

Help developers implement robust error handling:

- **HTTP Status Codes**: Correct status codes for API responses
- **Error Response Format**: Standardized error structure
- **Error Classes**: Custom error types for different scenarios
- **Frontend Error Handling**: User-friendly error messages
- **Logging**: Error logging best practices

## When to Use This Skill

Use this skill when:

- Implementing API error responses
- Creating custom error classes
- Handling errors in frontend components
- Logging errors for debugging
- Displaying error messages to users

## Core Principles

### 1. HTTP Status Codes

Use correct HTTP status codes for API responses:

```typescript
// 400 Bad Request - Client error (validation, malformed request)
if (!isValidEmail(email)) {
  return reply.code(400).send({
    error: {
      code: 'VALIDATION_ERROR',
      message: 'Invalid email format',
      details: { field: 'email' }
    }
  });
}

// 401 Unauthorized - Missing or invalid authentication
if (!request.headers['x-clerk-user-id']) {
  return reply.code(401).send({
    error: {
      code: 'UNAUTHORIZED',
      message: 'Authentication required'
    }
  });
}

// 403 Forbidden - Valid auth but insufficient permissions
if (!canAccessResource(userId, resourceId)) {
  return reply.code(403).send({
    error: {
      code: 'FORBIDDEN',
      message: 'You do not have permission to access this resource'
    }
  });
}

// 404 Not Found - Resource doesn't exist
const job = await repository.getById(id);
if (!job) {
  return reply.code(404).send({
    error: {
      code: 'NOT_FOUND',
      message: 'Job not found'
    }
  });
}

// 409 Conflict - Resource state conflict
const existing = await repository.findByEmail(email);
if (existing) {
  return reply.code(409).send({
    error: {
      code: 'CONFLICT',
      message: 'User with this email already exists'
    }
  });
}

// 422 Unprocessable Entity - Semantic validation error
if (application.stage === 'closed') {
  return reply.code(422).send({
    error: {
      code: 'INVALID_STATE',
      message: 'Cannot update closed application'
    }
  });
}

// 429 Too Many Requests - Rate limit exceeded
if (rateLimitExceeded) {
  return reply.code(429).send({
    error: {
      code: 'RATE_LIMIT_EXCEEDED',
      message: 'Too many requests, please try again later',
      retryAfter: 60
    }
  });
}

// 500 Internal Server Error - Unexpected server error
catch (error) {
  console.error('Unexpected error:', error);
  return reply.code(500).send({
    error: {
      code: 'INTERNAL_SERVER_ERROR',
      message: 'An unexpected error occurred'
    }
  });
}

// 503 Service Unavailable - External dependency failure
if (!canConnectToDatabase) {
  return reply.code(503).send({
    error: {
      code: 'SERVICE_UNAVAILABLE',
      message: 'Database unavailable, please try again later'
    }
  });
}
```

See [references/http-status-codes.md](./references/http-status-codes.md).

### 2. Error Response Format

All error responses follow standard envelope:

```typescript
{
  "error": {
    "code": "ERROR_CODE",           // Machine-readable error code
    "message": "User-friendly message", // Human-readable description
    "details"?: { ... },             // Optional additional context
    "retryAfter"?: number            // Optional retry delay (seconds)
  }
}
```

**Examples**:

```typescript
// Validation error with field details
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": {
      "fields": {
        "email": "Invalid email format",
        "phone": "Phone number is required"
      }
    }
  }
}

// Not found error
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Candidate not found"
  }
}

// Permission error
{
  "error": {
    "code": "FORBIDDEN",
    "message": "You do not have permission to delete this job"
  }
}

// Rate limit error
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many API requests",
    "retryAfter": 60
  }
}
```

See [examples/error-responses.ts](./examples/error-responses.ts).

### 3. Custom Error Classes

Create typed error classes for different scenarios:

```typescript
// Base application error
export class AppError extends Error {
    constructor(
        public code: string,
        message: string,
        public statusCode: number = 500,
        public details?: any,
    ) {
        super(message);
        this.name = "AppError";
    }
}

// Validation errors (400)
export class ValidationError extends AppError {
    constructor(message: string, details?: any) {
        super("VALIDATION_ERROR", message, 400, details);
        this.name = "ValidationError";
    }
}

// Not found errors (404)
export class NotFoundError extends AppError {
    constructor(resource: string) {
        super("NOT_FOUND", `${resource} not found`, 404);
        this.name = "NotFoundError";
    }
}

// Permission errors (403)
export class ForbiddenError extends AppError {
    constructor(message: string = "Access denied") {
        super("FORBIDDEN", message, 403);
        this.name = "ForbiddenError";
    }
}

// Conflict errors (409)
export class ConflictError extends AppError {
    constructor(message: string) {
        super("CONFLICT", message, 409);
        this.name = "ConflictError";
    }
}

// State errors (422)
export class InvalidStateError extends AppError {
    constructor(message: string) {
        super("INVALID_STATE", message, 422);
        this.name = "InvalidStateError";
    }
}

// Usage
throw new NotFoundError("Job");
throw new ValidationError("Invalid email", { field: "email" });
throw new ForbiddenError("Only recruiters can submit candidates");
throw new ConflictError("Application already exists");
throw new InvalidStateError("Cannot reopen closed job");
```

See [examples/error-classes.ts](./examples/error-classes.ts).

### 4. Error Handler Middleware

Fastify error handler catches all errors:

```typescript
// services/ats-service/src/index.ts
app.setErrorHandler((error, request, reply) => {
    // Log error with context
    console.error("Error handling request:", {
        method: request.method,
        url: request.url,
        error: error.message,
        stack: error.stack,
        userId: request.headers["x-clerk-user-id"],
    });

    // Handle custom AppError
    if (error instanceof AppError) {
        return reply.code(error.statusCode).send({
            error: {
                code: error.code,
                message: error.message,
                details: error.details,
            },
        });
    }

    // Handle Fastify validation errors
    if (error.validation) {
        return reply.code(400).send({
            error: {
                code: "VALIDATION_ERROR",
                message: "Request validation failed",
                details: error.validation,
            },
        });
    }

    // Handle Supabase errors
    if (error.code?.startsWith("PGRST")) {
        return reply.code(500).send({
            error: {
                code: "DATABASE_ERROR",
                message: "Database operation failed",
            },
        });
    }

    // Fallback to 500 for unexpected errors
    return reply.code(500).send({
        error: {
            code: "INTERNAL_SERVER_ERROR",
            message: "An unexpected error occurred",
        },
    });
});
```

See [examples/error-middleware.ts](./examples/error-middleware.ts).

### 5. Frontend Error Handling

Handle API errors gracefully in frontend:

```typescript
'use client';

import { useState } from 'react';
import { apiClient } from '@/lib/api-client';

export default function JobForm() {
  const [error, setError] = useState<string | null>(null);
  const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
  const [submitting, setSubmitting] = useState(false);

  async function handleSubmit(data: any) {
    setError(null);
    setFieldErrors({});
    setSubmitting(true);

    try {
      await apiClient.post('/jobs', data);
      // Success handling...
    } catch (err: any) {
      // Network error
      if (!err.response) {
        setError('Network error. Please check your connection.');
        return;
      }

      const { error } = err.response.data;

      // Validation error with field details
      if (error.code === 'VALIDATION_ERROR' && error.details?.fields) {
        setFieldErrors(error.details.fields);
        setError('Please fix the validation errors below.');
      }
      // Permission error
      else if (error.code === 'FORBIDDEN') {
        setError('You do not have permission to create jobs.');
      }
      // Generic error
      else {
        setError(error.message || 'An unexpected error occurred.');
      }
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* Global error alert */}
      {error && (
        <div className="alert alert-error mb-4">
          <i className="fa-duotone fa-regular fa-circle-exclamation"></i>
          <span>{error}</span>
        </div>
      )}

      {/* Field with error */}
      <fieldset className="fieldset">
        <legend className="fieldset-legend">Job Title *</legend>
        <input
          type="text"
          className={`input w-full ${fieldErrors.title ? 'input-error' : ''}`}
          name="title"
        />
        {fieldErrors.title && (
          <p className="fieldset-label text-error">{fieldErrors.title}</p>
        )}
      </fieldset>

      <button type="submit" className="btn btn-primary" disabled={submitting}>
        {submitting ? 'Creating...' : 'Create Job'}
      </button>
    </form>
  );
}
```

See [examples/frontend-error-handling.tsx](./examples/frontend-error-handling.tsx).

### 6. Error Logging

Log errors with context for debugging:

```typescript
// Backend error logging
function logError(
    error: Error,
    context: {
        service: string;
        method: string;
        userId?: string;
        resourceId?: string;
    },
): void {
    console.error("Error:", {
        service: context.service,
        method: context.method,
        userId: context.userId,
        resourceId: context.resourceId,
        error: {
            name: error.name,
            message: error.message,
            stack: error.stack,
        },
        timestamp: new Date().toISOString(),
    });
}

// Usage
try {
    await repository.update(id, data);
} catch (error) {
    logError(error as Error, {
        service: "ats-service",
        method: "JobRepository.update",
        userId: clerkUserId,
        resourceId: id,
    });
    throw error;
}
```

**Logging Rules**:

- ✅ Log all 500 errors with full context
- ✅ Include user ID and resource ID
- ✅ Include timestamp
- ✅ Include stack trace
- ⚠️ Log 400-level errors at info/warn level (not error)
- ❌ Don't log sensitive data (passwords, tokens)

See [examples/error-logging.ts](./examples/error-logging.ts).

### 7. Async Error Handling

Handle errors in async operations:

```typescript
// Try-catch for async functions
async function fetchJob(id: string): Promise<Job> {
    try {
        const { data, error } = await supabase
            .from("jobs")
            .select("*")
            .eq("id", id)
            .single();

        if (error) throw error;
        if (!data) throw new NotFoundError("Job");

        return data;
    } catch (error) {
        // Log error
        console.error("Failed to fetch job:", error);
        throw error; // Re-throw for caller to handle
    }
}

// Promise.allSettled for parallel operations
async function fetchMultipleJobs(ids: string[]): Promise<Job[]> {
    const results = await Promise.allSettled(ids.map((id) => fetchJob(id)));

    const jobs = results
        .filter(
            (r): r is PromiseFulfilledResult<Job> => r.status === "fulfilled",
        )
        .map((r) => r.value);

    const errors = results
        .filter((r): r is PromiseRejectedResult => r.status === "rejected")
        .map((r) => r.reason);

    if (errors.length > 0) {
        console.warn(`Failed to fetch ${errors.length} jobs:`, errors);
    }

    return jobs;
}
```

See [examples/async-error-handling.ts](./examples/async-error-handling.ts).

### 8. Database Error Handling

Handle Supabase/PostgreSQL errors:

```typescript
async function createJob(data: JobCreate): Promise<Job> {
    try {
        const { data: job, error } = await supabase
            .from("jobs")
            .insert(data)
            .select()
            .single();

        if (error) {
            // Handle specific error codes
            switch (error.code) {
                case "23505": // Unique constraint violation
                    throw new ConflictError(
                        "Job with this title already exists",
                    );

                case "23503": // Foreign key violation
                    throw new ValidationError("Invalid company ID");

                case "23502": // Not null violation
                    throw new ValidationError("Missing required field");

                case "PGRST116": // Not found
                    throw new NotFoundError("Job");

                default:
                    console.error("Database error:", error);
                    throw new AppError(
                        "DATABASE_ERROR",
                        "Database operation failed",
                    );
            }
        }

        return job;
    } catch (error) {
        if (error instanceof AppError) throw error;

        console.error("Unexpected database error:", error);
        throw new AppError("DATABASE_ERROR", "Database operation failed");
    }
}
```

See [examples/database-error-handling.ts](./examples/database-error-handling.ts) and [references/supabase-error-codes.md](./references/supabase-error-codes.md).

## Error Code Catalog

### Client Errors (4xx)

- `VALIDATION_ERROR` (400) - Request validation failed
- `UNAUTHORIZED` (401) - Authentication required
- `FORBIDDEN` (403) - Insufficient permissions
- `NOT_FOUND` (404) - Resource not found
- `CONFLICT` (409) - Resource state conflict
- `INVALID_STATE` (422) - Invalid resource state
- `RATE_LIMIT_EXCEEDED` (429) - Rate limit exceeded

### Server Errors (5xx)

- `INTERNAL_SERVER_ERROR` (500) - Unexpected server error
- `DATABASE_ERROR` (500) - Database operation failed
- `SERVICE_UNAVAILABLE` (503) - External service unavailable

See [references/error-codes.md](./references/error-codes.md).

## Testing Error Handling

Test error scenarios:

```typescript
describe("JobRepository", () => {
    it("should throw NotFoundError for non-existent job", async () => {
        mockSupabase.single.mockResolvedValue({ data: null, error: null });

        await expect(repository.getById("999")).rejects.toThrow(NotFoundError);
    });

    it("should throw ConflictError for duplicate job", async () => {
        mockSupabase.insert.mockResolvedValue({
            data: null,
            error: { code: "23505" },
        });

        await expect(repository.create(jobData)).rejects.toThrow(ConflictError);
    });

    it("should return 404 for non-existent job", async () => {
        const response = await app.inject({
            method: "GET",
            url: "/api/v2/jobs/non-existent-id",
        });

        expect(response.statusCode).toBe(404);
        expect(JSON.parse(response.body)).toMatchObject({
            error: {
                code: "NOT_FOUND",
                message: expect.any(String),
            },
        });
    });
});
```

See [examples/error-testing.ts](./examples/error-testing.ts).

## Anti-Patterns to Avoid

### ❌ Swallowing Errors

```typescript
// WRONG - Silent failure
try {
    await saveData();
} catch (error) {
    // Do nothing - error is lost!
}

// CORRECT - Log and handle
try {
    await saveData();
} catch (error) {
    console.error("Failed to save data:", error);
    throw error; // Or handle appropriately
}
```

### ❌ Generic Error Messages

```typescript
// WRONG - Unhelpful
throw new Error("Something went wrong");

// CORRECT - Specific
throw new ValidationError("Email format is invalid");
```

### ❌ Exposing Stack Traces to Users

```typescript
// WRONG - Security risk
return reply.code(500).send({
    error: error.stack, // Exposes internal details!
});

// CORRECT - Generic message
return reply.code(500).send({
    error: {
        code: "INTERNAL_SERVER_ERROR",
        message: "An unexpected error occurred",
    },
});
```

### ❌ Not Using Status Codes

```typescript
// WRONG - Always 200
return reply.send({
    success: false,
    error: "Not found",
});

// CORRECT - Use status code
return reply.code(404).send({
    error: {
        code: "NOT_FOUND",
        message: "Job not found",
    },
});
```

## References

- [Error Classes](./examples/error-classes.ts)
- [Error Responses](./examples/error-responses.ts)
- [Error Middleware](./examples/error-middleware.ts)
- [Frontend Error Handling](./examples/frontend-error-handling.tsx)
- [Database Error Handling](./examples/database-error-handling.ts)
- [Async Error Handling](./examples/async-error-handling.ts)
- [Error Testing](./examples/error-testing.ts)
- [HTTP Status Codes](./references/http-status-codes.md)
- [Error Codes Catalog](./references/error-codes.md)
- [Supabase Error Codes](./references/supabase-error-codes.md)

## Related Skills

- `api-specifications` - API response format standards
- `database-patterns` - Database error handling
- `testing-patterns` - Testing error scenarios

Related Skills

error-tracking

16
from diegosouzapw/awesome-omni-skill

Add error tracking and performance monitoring to your project services. Use this skill when adding error handling, creating new controllers/routes, instrumenting background jobs, or tracking performance. Supports Sentry, Datadog, and other monitoring solutions. ALL ERRORS MUST BE CAPTURED - no exceptions.

error-root-analyzer

16
from diegosouzapw/awesome-omni-skill

Comprehensive error analysis and root cause resolution. Use when programs fail, crash, or produce errors during execution. This skill performs deep debugging by identifying root causes (not just surface-level symptoms), conducting thorough module reviews to uncover related bugs and exceptions, and implementing holistic fixes that address all discovered issues.

error-handling-patterns

16
from diegosouzapw/awesome-omni-skill

Master error handling patterns across languages including exceptions, Result types, error propagation, and graceful degradation to build resilient applications. Use when implementing error handling, designing APIs, or improving application reliability.

error-diagnostics-error-analysis

16
from diegosouzapw/awesome-omni-skill

You are an expert error analysis specialist with deep expertise in debugging distributed systems, analyzing production incidents, and implementing comprehensive observability solutions.

error-debugging-error-analysis

16
from diegosouzapw/awesome-omni-skill

You are an expert error analysis specialist with deep expertise in debugging distributed systems, analyzing production incidents, and implementing comprehensive observability solutions.

adk-agent-handling

16
from diegosouzapw/awesome-omni-skill

Google ADK (Agent Development Kit) multi-agent system architecture for BigQuery data analytics. Covers BigQuery agent vs conversational agent patterns, ADK Single Parent Rule, domain routing with sub-agents, agent selection mechanisms, SQL error recovery with ReflectAndRetryToolPlugin, transfer_to_agent workflows, and frontend-backend agent coordination. Use when working with Google ADK agents, multi-agent systems, BigQuery SQL automation, domain expert routing, agent orchestration, or implementing error recovery strategies in AI agent applications.

add-error-type

16
from diegosouzapw/awesome-omni-skill

Add a new custom error type for domain-specific errors. Use when creating errors for specific business rules or HTTP status codes. Triggers on "add error", "custom error", "error type".

1k-error-handling

16
from diegosouzapw/awesome-omni-skill

Error handling patterns and best practices for OneKey. Use when implementing try/catch blocks, handling async errors, showing error messages, or managing error states in UI. Triggers on error, try, catch, exception, throw, fail, failure, error handling, error boundary, useAsyncCall, toast, fallback, error state.

gdpr-data-handling

16
from diegosouzapw/awesome-omni-skill

Implement GDPR-compliant data handling with consent management, data subject rights, and privacy by design. Use when building systems that process EU personal data, implementing privacy controls, o...

error-diagnostics-smart-debug

16
from diegosouzapw/awesome-omni-skill

Use when working with error diagnostics smart debug

error-detective

16
from diegosouzapw/awesome-omni-skill

Search logs and codebases for error patterns, stack traces, and anomalies. Correlates errors across systems and identifies root causes. Use PROACTIVELY when debugging issues, analyzing logs, or investigating production errors.

error-debugging-multi-agent-review

16
from diegosouzapw/awesome-omni-skill

Use when working with error debugging multi agent review