What are the best practices for defining error responses in OpenAPI?

What are the best practices for defining error responses in OpenAPI? #

Error responses are a critical part of any API contract. A consumer encountering an error needs to understand what went wrong, why it happened, and ideally what to do about it. Yet error responses are often the most neglected part of an OpenAPI document — frequently undocumented, inconsistently structured, or documented only for the happy path. This article covers best practices for defining error responses in OpenAPI that give consumers the information they need to build resilient integrations.

Why Error Documentation Matters #

When error responses are poorly documented:

  • Client developers guess at how to handle errors, leading to brittle error-handling code that breaks when new error formats appear.
  • Support teams spend more time investigating integration issues that would be self-service if the error format were well-documented.
  • SDKs and code generators produce incomplete error handling because the OpenAPI document lacks error response schemas.
  • API governance tools cannot audit error coverage because there’s nothing to audit.

Comprehensive error documentation in OpenAPI is an investment in the developer experience of everyone who consumes the API.

Document All Relevant HTTP Status Codes #

Every OpenAPI operation should document every HTTP status code it can realistically return. The responses object uses status codes as keys:

paths:
  /users/{id}:
    get:
      summary: Get a user by ID
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: The user was found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        "400":
          description: Invalid user ID format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "401":
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "403":
          description: Insufficient permissions to access this user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "404":
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "429":
          description: Rate limit exceeded
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        "500":
          description: Unexpected server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

A common mistake is documenting only 200 and 404. Operations should document at minimum: 400 (bad request), 401 (unauthenticated), 403 (unauthorized), 404 (not found where applicable), 429 (rate limited), and 500 (server error).

Use a Consistent Error Schema #

All error responses should use the same (or structurally similar) schema. Inconsistent error formats across endpoints force consumers to implement different error-handling logic for each operation — a major developer experience failure.

Minimal Error Schema #

components:
  schemas:
    Error:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
          description: A machine-readable error code
          example: "USER_NOT_FOUND"
        message:
          type: string
          description: A human-readable description of the error
          example: "No user found with the provided ID"

Rich Error Schema with Details #

For APIs that need to communicate validation errors across multiple fields:

components:
  schemas:
    Error:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
          example: "VALIDATION_ERROR"
        message:
          type: string
          example: "The request body contains invalid fields"
        details:
          type: array
          items:
            $ref: '#/components/schemas/ErrorDetail'
        requestId:
          type: string
          description: Unique identifier for this request, for support reference
          example: "req_4e8f9a2b"

    ErrorDetail:
      type: object
      required: [field, message]
      properties:
        field:
          type: string
          description: The field that caused the error
          example: "email"
        message:
          type: string
          description: Description of the validation failure
          example: "Must be a valid email address"
        code:
          type: string
          example: "INVALID_FORMAT"

Adopt RFC 9457 Problem Details #

RFC 9457 (formerly RFC 7807) defines a standard JSON format for HTTP API error responses called Problem Details. Adopting it means consumers can handle errors from any RFC 9457-compliant API using the same error model:

components:
  schemas:
    ProblemDetails:
      type: object
      properties:
        type:
          type: string
          format: uri
          description: A URI identifying the problem type
          example: "https://api.example.com/errors/user-not-found"
        title:
          type: string
          description: A short, human-readable summary of the problem type
          example: "User Not Found"
        status:
          type: integer
          description: The HTTP status code
          example: 404
        detail:
          type: string
          description: A human-readable explanation specific to this occurrence
          example: "No user with ID 'abc123' exists"
        instance:
          type: string
          format: uri
          description: A URI identifying this specific occurrence of the problem
          example: "/users/abc123"

The type URI is a stable, linkable identifier for the error type — ideally pointing to documentation that explains the error and how to resolve it. This makes Problem Details self-documenting in a way that opaque numeric error codes are not.

Use default for Catch-All Error Responses #

OpenAPI supports a special default response key that matches any HTTP status code not explicitly listed. This is useful for documenting a generic server error response without listing every possible 5xx code:

responses:
  "200":
    description: OK
  "404":
    description: Not found
  default:
    description: An unexpected error occurred
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/Error'

The default key complements explicit status codes — it does not replace specific ones.

Define Reusable Error Responses in components #

For APIs with many operations, defining error responses once in components/responses and referencing them with $ref keeps the document DRY and ensures consistency:

components:
  responses:
    BadRequest:
      description: The request is malformed or contains invalid parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalidId:
              summary: Invalid ID format
              value:
                code: "INVALID_PARAMETER"
                message: "The provided ID must be a UUID"

    Unauthorized:
      description: Authentication is required or the token is invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    Forbidden:
      description: The authenticated user lacks permission for this operation
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    NotFound:
      description: The requested resource was not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    TooManyRequests:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    InternalServerError:
      description: An unexpected server error occurred
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

Operations then reference these with $ref:

responses:
  "200":
    ...
  "400":
    $ref: '#/components/responses/BadRequest'
  "401":
    $ref: '#/components/responses/Unauthorized'
  "404":
    $ref: '#/components/responses/NotFound'
  "429":
    $ref: '#/components/responses/TooManyRequests'
  default:
    $ref: '#/components/responses/InternalServerError'

Include Error Examples #

Examples in error responses help consumers understand exactly what an error looks like in practice:

"404":
  description: User not found
  content:
    application/json:
      schema:
        $ref: '#/components/schemas/Error'
      examples:
        userNotFound:
          summary: User not found
          value:
            code: "USER_NOT_FOUND"
            message: "No user found with ID 'f47ac10b'"
            requestId: "req_7g3h8k2m"

Conclusion #

Well-documented error responses in OpenAPI are as important as success response documentation. By defining a consistent error schema, adopting RFC 9457 Problem Details, documenting all relevant HTTP status codes, centralizing error response definitions in components/responses, and including concrete examples, API teams give consumers everything they need to build robust, resilient integrations. An API whose errors are as well-designed and documented as its success paths is an API that earns its consumers’ trust.


Last updated on April 30, 2026.

This website is not affiliated with the OpenAPI Initiative.