🎯

exception-taxonomy

🎯Skill

from dadbodgeoff/drift

VibeIndex|
What it does

Defines a structured, hierarchical exception system with standardized error codes, HTTP status mappings, and consistent API error responses.

πŸ“¦

Part of

dadbodgeoff/drift(69 items)

exception-taxonomy

Installation

npm installInstall npm package
npm install -g driftdetect
npm installInstall npm package
npm install -g driftdetect@latest
npm installInstall npm package
npm install -g driftdetect-mcp
Claude Desktop ConfigurationAdd this to your claude_desktop_config.json
{ "mcpServers": { "drift": { "command": "driftdetect-mcp" } } ...
πŸ“– Extracted from docs: dadbodgeoff/drift
5Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Hierarchical exception system with HTTP status codes, machine-readable error codes, and structured responses for consistent API error handling across all endpoints.

Overview

# Exception Taxonomy

Hierarchical exception system with HTTP status codes, error codes, and structured responses for consistent API error handling.

When to Use This Skill

  • Building APIs that need consistent error responses
  • Creating machine-readable error codes for client handling
  • Implementing retry logic based on error types
  • Standardizing error handling across a large codebase

Core Concepts

A well-designed exception taxonomy provides:

  • Consistent error responses across all endpoints
  • Machine-readable error codes for client handling
  • Human-readable messages for debugging
  • HTTP status code mapping
  • Retry hints for transient failures

The hierarchy typically follows:

```

BaseAppError (abstract)

β”œβ”€β”€ AuthenticationError (401)

β”œβ”€β”€ AuthorizationError (403)

β”œβ”€β”€ ResourceError (404/409)

β”œβ”€β”€ ValidationError (422)

β”œβ”€β”€ RateLimitError (429)

β”œβ”€β”€ ExternalServiceError (502/503)

└── PaymentError (402)

```

Implementation

Python

```python

from dataclasses import dataclass, field

from typing import Optional, Dict, Any

from enum import Enum

class ErrorCode(str, Enum):

"""Standardized error codes for API responses."""

# Authentication

AUTH_INVALID_CREDENTIALS = "AUTH_INVALID_CREDENTIALS"

AUTH_TOKEN_EXPIRED = "AUTH_TOKEN_EXPIRED"

AUTH_TOKEN_INVALID = "AUTH_TOKEN_INVALID"

AUTH_EMAIL_EXISTS = "AUTH_EMAIL_EXISTS"

# Authorization

FORBIDDEN = "FORBIDDEN"

# Resources

RESOURCE_NOT_FOUND = "RESOURCE_NOT_FOUND"

RESOURCE_CONFLICT = "RESOURCE_CONFLICT"

# Rate Limiting

RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED"

# External Services

GENERATION_FAILED = "GENERATION_FAILED"

GENERATION_TIMEOUT = "GENERATION_TIMEOUT"

# Validation

VALIDATION_ERROR = "VALIDATION_ERROR"

INVALID_STATE_TRANSITION = "INVALID_STATE_TRANSITION"

@dataclass

class BaseAppError(Exception):

"""Base exception for all application errors."""

message: str

code: ErrorCode

status_code: int = 500

details: Optional[Dict[str, Any]] = field(default_factory=dict)

retry_after: Optional[int] = None

def __post_init__(self):

super().__init__(self.message)

def to_dict(self) -> Dict[str, Any]:

"""Convert to API response format."""

error_dict = {

"error": {

"message": self.message,

"code": self.code.value,

}

}

if self.details:

error_dict["error"]["details"] = self.details

if self.retry_after is not None:

error_dict["error"]["retry_after"] = self.retry_after

return error_dict

@dataclass

class NotFoundError(BaseAppError):

"""Resource not found error."""

resource_type: str = "resource"

resource_id: str = ""

message: str = field(init=False)

code: ErrorCode = field(default=ErrorCode.RESOURCE_NOT_FOUND)

status_code: int = 404

def __post_init__(self):

self.message = f"{self.resource_type.title()} not found"

self.details = {

"resource_type": self.resource_type,

"resource_id": self.resource_id,

}

super().__post_init__()

@dataclass

class RateLimitError(BaseAppError):

"""Rate limit exceeded error."""

retry_after: int = 60

message: str = "Rate limit exceeded"

code: ErrorCode = field(default=ErrorCode.RATE_LIMIT_EXCEEDED)

status_code: int = 429

def __post_init__(self):

self.details = {"retry_after": self.retry_after}

super().__post_init__()

@dataclass

class InvalidStateTransitionError(BaseAppError):

"""Invalid state transition error."""

current_status: str = ""

target_status: str = ""

message: str = field(init=False)

code: ErrorCode = field(default=ErrorCode.INVALID_STATE_TRANSITION)

status_code: int = 409

def __post_init__(self):

self.message = f"Cannot transition from '{self.current_status}' to '{self.target_status}'"

self.details = {

"current_status": self.current_status,

"target_status": self.target_status,

}

super().__post_init__()

```

TypeScript

```typescript

export enum ErrorCode {

AUTH_INVALID_CREDENTIALS = 'AUTH_INVALID_CREDENTIALS',

AUTH_TOKEN_EXPIRED = 'AUTH_TOKEN_EXPIRED',

AUTH_TOKEN_INVALID = 'AUTH_TOKEN_INVALID',

FORBIDDEN = 'FORBIDDEN',

RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',

RESOURCE_CONFLICT = 'RESOURCE_CONFLICT',

RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',

VALIDATION_ERROR = 'VALIDATION_ERROR',

INVALID_STATE_TRANSITION = 'INVALID_STATE_TRANSITION',

}

interface ErrorDetails {

[key: string]: unknown;

}

export class BaseAppError extends Error {

constructor(

public readonly message: string,

public readonly code: ErrorCode,

public readonly statusCode: number = 500,

public readonly details: ErrorDetails = {},

public readonly retryAfter?: number

) {

super(message);

this.name = this.constructor.name;

}

toJSON() {

const error: Record = {

message: this.message,

code: this.code,

};

if (Object.keys(this.details).length > 0) {

error.details = this.details;

}

if (this.retryAfter !== undefined) {

error.retry_after = this.retryAfter;

}

return { error };

}

}

export class NotFoundError extends BaseAppError {

constructor(resourceType: string, resourceId: string) {

super(

${resourceType.charAt(0).toUpperCase() + resourceType.slice(1)} not found,

ErrorCode.RESOURCE_NOT_FOUND,

404,

{ resource_type: resourceType, resource_id: resourceId }

);

}

}

export class RateLimitError extends BaseAppError {

constructor(retryAfter: number = 60) {

super(

'Rate limit exceeded',

ErrorCode.RATE_LIMIT_EXCEEDED,

429,

{ retry_after: retryAfter },

retryAfter

);

}

}

export class InvalidStateTransitionError extends BaseAppError {

constructor(currentStatus: string, targetStatus: string) {

super(

Cannot transition from '${currentStatus}' to '${targetStatus}',

ErrorCode.INVALID_STATE_TRANSITION,

409,

{ current_status: currentStatus, target_status: targetStatus }

);

}

}

```

Usage Examples

FastAPI Exception Handlers

```python

from fastapi import FastAPI, Request

from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(BaseAppError)

async def app_error_handler(request: Request, exc: BaseAppError) -> JSONResponse:

headers = {"Retry-After": str(exc.retry_after)} if exc.retry_after else None

return JSONResponse(

status_code=exc.status_code,

content=exc.to_dict(),

headers=headers,

)

@app.exception_handler(Exception)

async def generic_error_handler(request: Request, exc: Exception) -> JSONResponse:

logger.exception(f"Unexpected error: {exc}")

return JSONResponse(

status_code=500,

content={"error": {"message": "An unexpected error occurred", "code": "INTERNAL_ERROR"}},

)

```

Route Usage

```python

@router.get("/jobs/{job_id}")

async def get_job(job_id: str, user_id: str = Depends(get_current_user)):

job = await job_service.get(job_id)

if not job:

raise NotFoundError(resource_type="job", resource_id=job_id)

if job.user_id != user_id:

raise AuthorizationError(resource_type="job")

return job

```

Client-Side Handling (TypeScript)

```typescript

interface APIError {

error: {

message: string;

code: string;

details?: Record;

retry_after?: number;

};

}

function handleAPIError(error: APIError): void {

switch (error.error.code) {

case 'AUTH_TOKEN_EXPIRED':

authStore.refreshToken();

break;

case 'RATE_LIMIT_EXCEEDED':

const retryAfter = error.error.retry_after || 60;

toast.error(Rate limited. Try again in ${retryAfter}s);

break;

default:

toast.error(error.error.message);

}

}

```

Best Practices

  1. Use specific exceptions - Create domain-specific exceptions rather than generic ones
  2. Include context - Always include relevant IDs and state in error details
  3. Map to HTTP codes - Each exception should have a clear HTTP status code
  4. Provide retry hints - For transient failures, include retry_after
  5. Use error codes - Machine-readable codes enable client-side handling logic
  6. Log appropriately - Log full details server-side, return safe messages to clients

Common Mistakes

  • Using generic exceptions instead of domain-specific ones
  • Forgetting to include resource IDs in error details
  • Not providing retry hints for rate limit errors
  • Exposing internal error details in production responses
  • Inconsistent error response formats across endpoints

Related Patterns

  • error-sanitization - Sanitize errors before returning to users
  • error-handling - General error handling patterns
  • rate-limiting - Rate limiting implementation

More from this repository10

🎯
feature-flags🎯Skill

Enables controlled feature rollouts, A/B testing, and selective feature access through configurable flags for gradual deployment and user targeting.

🎯
design-tokens🎯Skill

Generates a comprehensive, type-safe design token system with WCAG AA color compliance and multi-framework support for consistent visual design.

🎯
file-uploads🎯Skill

Securely validates, scans, and processes file uploads with multi-stage checks, malware detection, and race condition prevention.

🎯
ai-coaching🎯Skill

Guides users through articulating creative intent by extracting structured parameters and detecting conversation readiness.

🎯
environment-config🎯Skill

Validates and centralizes environment variables with type safety, fail-fast startup checks, and multi-environment support.

🎯
community-feed🎯Skill

Generates efficient social feed with cursor pagination, trending algorithms, and engagement tracking for infinite scroll experiences.

🎯
cloud-storage🎯Skill

Enables secure, multi-tenant cloud file storage with signed URLs, direct uploads, and visibility control for user-uploaded assets.

🎯
email-service🎯Skill

Simplifies email sending, templating, and tracking with robust SMTP integration and support for multiple email providers and transactional workflows.

🎯
error-sanitization🎯Skill

Sanitizes error messages by logging full details server-side while exposing only generic, safe messages to prevent sensitive information leakage.

🎯
batch-processing🎯Skill

Optimizes database operations by collecting and batching independent records, improving throughput by 30-40% with built-in fallback processing.