Error responses follow one structure
Why it matters
When auth errors return { message: '...' }, validation errors return { errors: [...] }, and not-found errors return raw strings, consumers must write a different parser for each error type. This is not just inconvenient — it makes automated error handling (retry logic, error monitoring, user-facing messages) impossible to build generically. iso-25010:2011 compatibility.interoperability and maintainability.modifiability both require a stable, machine-readable error contract. Inconsistent errors are also a debugging liability: different shapes in different production logs make tracing failures significantly harder.
Severity rationale
Critical because inconsistent error shapes make automated error handling and observability impossible, and every new error type added to the API risks introducing yet another shape.
Remediation
Define one error structure and use it for every error response across every endpoint. Create a helper to enforce it:
interface ApiError {
error: {
code: string // machine-readable: "NOT_FOUND", "VALIDATION_ERROR"
message: string // human-readable description
details?: unknown // field-level detail for validation errors
}
}
function apiError(code: string, message: string, status: number, details?: unknown) {
return Response.json({ error: { code, message, details } }, { status })
}
// In every handler:
return apiError('NOT_FOUND', 'User not found', 404)
return apiError('VALIDATION_ERROR', 'Invalid request', 400, zodError.issues)
return apiError('INTERNAL_ERROR', 'Something went wrong', 500)
The code field is the key — it lets consumers branch on machine-readable values rather than parsing human-readable strings. See the status-codes check for HTTP code consistency.
Detection
-
ID:
error-schema -
Severity:
critical -
What to look for: Examine error responses across all endpoints. Check whether every error -- validation errors, not-found errors, auth errors, server errors -- follows the same structural shape. Common patterns:
{ error: { code: "NOT_FOUND", message: "User not found" } }or{ error: "message" }or{ message: "...", statusCode: 404 }. Look for inconsistency: some endpoints return{ error: "..." }, others return{ message: "..." }, others return raw strings or HTML error pages. Also check: do error responses include a machine-readable error code (not just the HTTP status)? -
Pass criteria: Enumerate all distinct error response shapes across the codebase. 100% of error responses across all endpoints must use the same structure. The structure includes at minimum a machine-readable code and a human-readable message. Validation errors include field-level detail. Report the count of distinct error shapes found even on pass.
-
Fail criteria: Error response shapes differ across endpoints -- some return
{ error: "..." }, others return{ message: "..." }, others return different structures. Or error responses are raw strings, HTML, or vary in structure by error type. Must not pass when more than 1 distinct error shape exists across the API. -
Skip (N/A) when: Never skip -- every API returns errors.
-
Detail on fail: Describe the inconsistency (e.g., "Auth errors return
{ message: '...' }, validation errors return{ errors: [...] }, not-found returns{ error: '...' }, and server errors return rawInternal Server Errorstring. 4 different error shapes across 12 endpoints."). Max 500 chars. -
Cross-reference: For HTTP status code usage consistency, see the
status-codescheck in the Developer Ergonomics category below. -
Remediation: Define a single error response structure and use it for ALL errors:
// Consistent error shape: interface ApiError { error: { code: string // machine-readable: "NOT_FOUND", "VALIDATION_ERROR" message: string // human-readable description details?: unknown // optional field-level detail for validation errors } } // Helper function: function apiError(code: string, message: string, status: number, details?: unknown) { return Response.json({ error: { code, message, details } }, { status }) } // Usage in every handler: return apiError('NOT_FOUND', 'User not found', 404) return apiError('VALIDATION_ERROR', 'Invalid request', 400, zodError.issues) return apiError('INTERNAL_ERROR', 'Something went wrong', 500)
External references
- iso-25010:2011 · compatibility.interoperability — Interoperability — uniform error schema enables generic error handling across consumers
- iso-25010:2011 · maintainability.modifiability
Taxons
History
- 2026-04-18·v1.0.0·Initial import from api-design·automated