Error responses that return only an HTTP status code with no body, or a plain string message with no machine-readable code, force API consumers to parse free-text to determine what went wrong — fragile, locale-sensitive, and impossible to handle programmatically. Returning stack traces or SQL fragments in production error bodies is a CWE-209 violation (information exposure through error messages) that leaks table names, column names, and internal architecture to any caller who triggers a 500. Consistent, structured error responses are required for ISO-25010:2011 compatibility.interoperability.
High because inconsistent or detail-leaking error responses both break API consumers and expose internal implementation details that assist attackers in crafting targeted exploits.
Create a centralized error helper at src/lib/api-errors.ts that produces a fixed response shape and call it from every route handler:
// src/lib/api-errors.ts
export const ERROR_CODES = {
VALIDATION_ERROR: 400,
NOT_FOUND: 404,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
RATE_LIMITED: 429,
INTERNAL_ERROR: 500,
} as const
export function apiError(code: keyof typeof ERROR_CODES, message: string) {
return Response.json(
{ error: { code, message } },
{ status: ERROR_CODES[code] }
)
}
Log the full error server-side (including stack trace) but never include it in the response body. Never return 200 with { success: false } — use the semantically correct HTTP status code so HTTP clients and monitoring tools can act on it.
saas-api-design.api-docs.error-responses-codes-messageshigh{ message: "Internal Server Error" } with no code, or bare HTTP status codes with no body."VALIDATION_ERROR", "NOT_FOUND", "RATE_LIMITED"), and a human-readable message. Shape is consistent across routes.POST /api/users returns 500 with no body. Describe the error handling gaps (e.g., "POST /api/users returns 500 with no body on validation error; stack traces returned in production error responses; no machine-readable error codes in any route"). Max 500 chars.src/lib/api-errors.ts. Define a fixed error response shape: { error: { code: "ERROR_CODE", message: "Human-readable message", details: {} } }. Create a centralized error handler or utility function that formats all errors into this shape. Use appropriate HTTP status codes — do not return 200 with { success: false } in the body (this breaks HTTP semantics and client error handling). Never return stack traces or internal error details in production — log them server-side instead. Common codes to define: VALIDATION_ERROR, NOT_FOUND, UNAUTHORIZED, FORBIDDEN, RATE_LIMITED, INTERNAL_ERROR.