#api#best-practice
When writing the OpenAPI specs of an API, which error case should I document in the "response" section ?
In OpenAPI you only need to describe the responses that the client can realistically expect and act on for a given operation. The spec itself clarifies that the document
“is not necessarily expected to cover all HTTP response codes … however, it is expected to cover a successful operation response and any known errors.” (swagger.io)
- Deterministic validation or business-rule failures
400 Bad Request or 422 Unprocessable Entity when the input is syntactically or semantically wrong. - Authentication / authorisation issues
401 Unauthorized, 403 Forbidden. - Resource state errors
404 Not Found, 409 Conflict (e.g., optimistic-lock conflict, duplicate data). - Rate limiting / quota
429 Too Many Requests (often includes aRetry-After
header). - Operation-specific conditions (e.g., 410 Gone, 412 Precondition Failed) that your service purposefully returns.
These are the cases where the caller can retry differently, show a message, or log something specific; therefore you should list them explicitly in the responses
section with both the status code and the error schema.
Unhandled exceptions, time-outs, or infrastructure failures are not predictable from the API contract. Best practice is to add one generic entry instead of enumerating every 5xx code:
responses:
'200':
description: Successful response
content:
application/json:
schema: { $ref: '#/components/schemas/MyData' }
'4XX': # or individual 4xx codes as above
$ref: '#/components/responses/ClientError'
'5XX': # ← catches 500, 502, 503, 504 …
description: Unexpected server error
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
Using the wildcard (4XX
, 5XX
) or the special default
response keeps the spec concise and still tells integrators that “something went wrong on the server” without promising a specific error payload. This mirrors the guidance echoed in community discussions: document the success path and the errors you can foresee, but you don’t have to list every hypothetical 5xx response individually. (stackoverflow.com)
Adopt a single structured format—most teams now use “Problem Details for HTTP APIs” (RFC 9457, successor to RFC 7807). It gives you a standard object with fields like type
, title
, status
, detail
, instance
, and is easily extended with code
, errors
, etc. (swagger.io) Define it once under components/schemas
and $ref
it from every error response.
TL;DR
Document:
- Success (2xx) responses.
- All client-actionable errors (specific 4xx and any business-domain errors).
- One generic server error using
default
,5XX
, or a single500
, tied to your standard error object.
Everything else can safely be left unspecified—clients already know they must handle unexpected HTTP codes defensively.
Below is a compact, copy-paste-ready scaffold that teams commonly put in components/schemas
and components/responses
. It follows RFC 9457 “Problem Details for HTTP APIs”, then shows how you might extend it with an application-specific code
and an array of per-field errors
(useful for validation failures).
components:
schemas:
ProblemDetails: # the base error object
type: object
required: [type, title, status]
description: Standard problem-details envelope (RFC 9457)
properties:
type:
type: string
format: uri
example: https://api.example.com/problems/out-of-credit
description: URI that identifies the problem “class”.
title:
type: string
example: Out of credit
description: Short, human-readable summary (usually from an error catalogue).
status:
type: integer
format: int32
example: 403
description: HTTP status code for this occurrence.
detail:
type: string
example: Your account balance is 0. Top-up required before purchase.
description: Human-readable explanation for *this* occurrence.
instance:
type: string
format: uri
example: urn:uuid:4b602e26-d2e2-4fc2-8b31-cc04a086fae2
description: URI that identifies this specific error record (for support logs).
# --- OPTIONAL EXTENSIONS ---
code:
type: string
example: ACC_001
description: Internal/business error code that never changes language.
errors:
type: array
description: List of granular validation problems (only used on 4 xx).
items:
$ref: '#/components/schemas/FieldError'
FieldError:
type: object
required: [field, message]
description: Fine-grained validation error for a single input element.
properties:
field:
type: string
example: email
message:
type: string
example: must be a valid email address
responses:
ClientError: # can be reused for 4xx codes you expose
description: Generic client error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
ServerError: # catch-all 5xx (or use `default`)
description: Unexpected server failure
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
{
"type": "https://api.example.com/problems/invalid-parameter",
"title": "Invalid parameter",
"status": 400,
"detail": "Query parameter 'sort' must be one of [asc, desc].",
"instance": "urn:uuid:4b602e26-d2e2-4fc2-8b31-cc04a086fae2",
"code": "VAL_002",
"errors": [
{
"field": "sort",
"message": "must be 'asc' or 'desc'"
}
]
}
type
,title
,status
,detail
,instance
are the five base members defined by RFC 9457; they let generic tooling parse the error while still giving humans context. (rfc-editor.org)code
(string) anderrors
(array) are extension members—allowed by the RFC as long as clients ignore fields they don’t understand. They help map the error to internal logs and convey multiple validation issues in one go. (swagger.io)
Use this schema as a single source of truth: every operation that can fail simply $ref
s the appropriate response (ClientError
, ServerError
, or something more specific like ValidationError
). That keeps your OpenAPI tidy while giving consumers a predictable, machine-readable error format.