/** * Error Codes Catalog — T-22 * * Centralized error code registry for consistent API error responses. * Each code has a category prefix, numeric ID, message, and HTTP status. * * Usage: * import { ERROR_CODES, createErrorResponse } from "@/shared/constants/errorCodes"; * return createErrorResponse("AUTH_001", { detail: "Token expired" }); * * @module shared/constants/errorCodes */ export interface ErrorCodeDef { code: string; message: string; httpStatus: number; category: string; } interface ErrorDetails { detail?: string; requestId?: string; retryAfter?: number; } export const ERROR_CODES: Record = { // ── Auth ── AUTH_001: { code: "AUTH_001", message: "Authentication required", httpStatus: 401, category: "AUTH", }, AUTH_002: { code: "AUTH_002", message: "Invalid API key", httpStatus: 401, category: "AUTH" }, AUTH_003: { code: "AUTH_003", message: "API key expired", httpStatus: 401, category: "AUTH" }, AUTH_004: { code: "AUTH_004", message: "Insufficient permissions", httpStatus: 403, category: "AUTH", }, AUTH_005: { code: "AUTH_005", message: "Account locked", httpStatus: 423, category: "AUTH" }, AUTH_006: { code: "AUTH_006", message: "No credentials for provider", httpStatus: 400, category: "AUTH", }, // ── Proxy ── PROXY_001: { code: "PROXY_001", message: "Proxy connection failed", httpStatus: 502, category: "PROXY", }, PROXY_002: { code: "PROXY_002", message: "Proxy timeout", httpStatus: 504, category: "PROXY" }, PROXY_003: { code: "PROXY_003", message: "All proxies exhausted", httpStatus: 503, category: "PROXY", }, // ── Rate Limiting ── RATE_001: { code: "RATE_001", message: "Rate limit exceeded", httpStatus: 429, category: "RATE_LIMIT", }, RATE_002: { code: "RATE_002", message: "Daily budget exceeded", httpStatus: 429, category: "RATE_LIMIT", }, RATE_003: { code: "RATE_003", message: "All accounts rate-limited", httpStatus: 503, category: "RATE_LIMIT", }, // ── Model / Routing ── MODEL_001: { code: "MODEL_001", message: "Model not found", httpStatus: 404, category: "MODEL" }, MODEL_002: { code: "MODEL_002", message: "Ambiguous model identifier", httpStatus: 400, category: "MODEL", }, MODEL_003: { code: "MODEL_003", message: "Model temporarily unavailable", httpStatus: 503, category: "MODEL", }, // ── Provider ── PROVIDER_001: { code: "PROVIDER_001", message: "Provider error", httpStatus: 502, category: "PROVIDER", }, PROVIDER_002: { code: "PROVIDER_002", message: "Provider timeout", httpStatus: 504, category: "PROVIDER", }, PROVIDER_003: { code: "PROVIDER_003", message: "Provider not configured", httpStatus: 400, category: "PROVIDER", }, // ── Validation ── VALID_001: { code: "VALID_001", message: "Invalid request body", httpStatus: 400, category: "VALIDATION", }, VALID_002: { code: "VALID_002", message: "Missing required field", httpStatus: 400, category: "VALIDATION", }, VALID_003: { code: "VALID_003", message: "Input sanitization blocked", httpStatus: 400, category: "VALIDATION", }, // ── Internal ── INTERNAL_001: { code: "INTERNAL_001", message: "Internal server error", httpStatus: 500, category: "INTERNAL", }, INTERNAL_002: { code: "INTERNAL_002", message: "Database error", httpStatus: 500, category: "INTERNAL", }, INTERNAL_003: { code: "INTERNAL_003", message: "Circuit breaker open", httpStatus: 503, category: "INTERNAL", }, }; export function createErrorResponse(code: string, details: ErrorDetails = {}) { const def = ERROR_CODES[code]; if (!def) { return { error: { code: "INTERNAL_001", message: `Unknown error code: ${code}`, category: "INTERNAL", }, status: 500, }; } const response: any = { error: { code: def.code, message: def.message, category: def.category, ...(details.detail ? { detail: details.detail } : {}), ...(details.requestId ? { requestId: details.requestId } : {}), }, status: def.httpStatus, }; if (details.retryAfter) { response.retryAfter = details.retryAfter; } return response; } export function getErrorsByCategory(category: string): ErrorCodeDef[] { return Object.values(ERROR_CODES).filter((e) => e.category === category); }