Spaces:
Runtime error
Runtime error
| /** | |
| * 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<string, ErrorCodeDef> = { | |
| // ββ 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", | |
| }, | |
| AUTH_007: { | |
| code: "AUTH_007", | |
| message: "Session expired (re-login required)", | |
| httpStatus: 401, | |
| 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", | |
| }, | |
| // ββ Combos (T-22.b β 400-shape standardization for /api/combos/{id}) ββ | |
| COMBO_001: { | |
| code: "COMBO_001", | |
| message: "Request body is not valid JSON", | |
| httpStatus: 400, | |
| category: "COMBO", | |
| }, | |
| COMBO_002: { | |
| code: "COMBO_002", | |
| message: "One or more combo fields are invalid", | |
| httpStatus: 400, | |
| category: "COMBO", | |
| }, | |
| COMBO_003: { | |
| code: "COMBO_003", | |
| message: "Composite tier configuration is invalid", | |
| httpStatus: 400, | |
| category: "COMBO", | |
| }, | |
| COMBO_004: { | |
| code: "COMBO_004", | |
| message: "A combo with this name already exists", | |
| httpStatus: 400, | |
| category: "COMBO", | |
| }, | |
| COMBO_005: { | |
| code: "COMBO_005", | |
| message: "Combo reference graph is invalid (cycle or excessive depth)", | |
| httpStatus: 400, | |
| category: "COMBO", | |
| }, | |
| COMBO_006: { | |
| code: "COMBO_006", | |
| message: "This combo is managed by Quota Share and cannot be modified here", | |
| httpStatus: 409, | |
| category: "COMBO", | |
| }, | |
| COMBO_007: { | |
| code: "COMBO_007", | |
| message: "Combo not found", | |
| httpStatus: 404, | |
| category: "COMBO", | |
| }, | |
| // ββ 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); | |
| } | |