HTTP error response schema OpenAPI example: patterns & tips

HTTP error response schema OpenAPI example: define robust error schemas & status codes in OpenAPI with YAML patterns for consistent API error handling.

Jump to section

Jump to section

Jump to section

Error response schemas in OpenAPI aren't just documentation—they're contracts that determine how your API communicates failure to clients. When you define these schemas properly, SDK generators can create strongly-typed exception classes, enabling developers to write catch (ValidationError e) instead of parsing generic JSON blobs.

This guide covers how to design consistent error schemas, choose appropriate HTTP status codes, implement standards like RFC 7807, and create reusable patterns that translate into better generated SDKs. You'll learn practical techniques for validation errors, authentication failures, rate limiting, and how these decisions impact everything from automatic retry logic to MCP server generation.

What are error response schemas in OpenAPI?

An error response schema in OpenAPI is a structured definition, using JSON Schema, that describes the body of an error your API returns for a specific HTTP status code. This isn’t just about documentation; it’s about creating a predictable contract for how your API communicates failure. When you create OpenAPI specs, this contract becomes machine-readable and enforceable. When clients—whether they’re human-written or machine-generated—know exactly what to expect, they can build more resilient integrations.

For example, a well-defined error schema allows an SDK generator to create strongly-typed exception classes. This means a developer using your SDK can write try...catch blocks that handle specific errors, like catch (InvalidInputError e), instead of parsing a generic JSON blob. This exemplifies why your API isn’t finished until the SDK ships. If a schema is missing or poorly defined, a good generator will raise a diagnostic warning, because it knows the resulting developer experience will be subpar.

Here is the most minimal example of a 404 Not Found response. It defines the status code and gives a simple text description of what it means.

paths:
  /users/{userId}:
    get:
      summary: Get a user by ID
      responses:
        '200':
          description: OK
          # ... schema for a successful response
        '404':
          description

Define HTTP error status codes

The HTTP status code is the first signal a client receives when something goes wrong. Choosing the right code is crucial because it dictates how clients, proxies, and SDKs should react.

Common client error codes (4xx range) and server error codes (5xx range) include:

  • 400 Bad Request: The server cannot process the request due to a client-side error, like malformed syntax.

  • 401 Unauthorized: The client must authenticate itself to get the requested response.

  • 403 Forbidden: The client does not have access rights to the content; authentication will not help.

  • 404 Not Found: The server cannot find the requested resource.

  • 422 Unprocessable Entity: The request was well-formed but was unable to be followed due to semantic errors.

  • 429 Too Many Requests: The user has sent too many requests in a given amount of time ('rate limiting').

  • 500 Internal Server Error: The server has encountered a situation it doesn't know how to handle.

  • 503 Service Unavailable: The server is not ready to handle the request, often due to maintenance or overload.

You can also use wildcards like 4XX or 5XX to define a response for a whole range of status codes. This is useful for grouping similar error types.

responses:
  '400':
    description: A validation error occurred.
  '401':
    description: An authentication error occurred.
  '4XX':
    description

This choice has real-world consequences for your users. For instance, a well-built SDK will automatically retry requests that fail with a 503 or 429 status code, but it should never retry a 400 error, as the request will just fail again. Defining your status codes clearly enables this intelligent behavior.

Design error response body schemas

After the status code, the response body provides the details. A consistent schema for this body is the key to a great developer experience.

Add basic fields

At a minimum, an error response should contain a machine-readable code and a human-readable message. You can define this directly in your endpoint definition.

'400':
  description: Invalid input provided.
  content:
    application/json:
      schema:
        type: object
        properties:
          code:
            type: string
            description: A unique code for this error type.
            example: 'invalid_parameter'
          message:
            type: string
            description: A human-readable message explaining the error.
            example: 'The \'email\' parameter must be a valid email address.'

Adopt RFC 7807

For better interoperability, consider adopting the "Problem Details for HTTP APIs" standard, defined in RFC 7807. It provides a standard way to carry machine-readable details of an error in an HTTP response. This avoids the need to invent your own error format.

A standard Problem Details object includes these fields:

  • type: A URI that identifies the problem type.

  • title: A short, human-readable summary of the problem type.

  • status: The HTTP status code generated by the origin server for this occurrence of the problem.

  • detail: A human-readable explanation specific to this occurrence of the problem.

  • instance: A URI that identifies the specific occurrence of the problem.

When you adopt a standard like this, SDK generators can map it directly to a single, consistent APIError class across every language, giving your users a predictable way to handle all API failures.

Add custom fields

You can extend a base schema like RFC 7807 with your own custom fields to provide even more context. A request_id is incredibly useful for debugging, as it allows you to correlate a specific error with your server logs.

# In components/schemas/Error.yaml
type: object
properties:
  # ... standard RFC 7807 fields
  request_id:
    type: string
    description: A unique identifier for the request, for tracing purposes.
    example: 'req_12345'
  errors:
    type: array
    items:
      type: object
      properties:
        field:
          type: string
        message:
          type

Create reusable error schemas

Defining error schemas inline for every endpoint is repetitive and error-prone. The best practice is to define a reusable error schema in the components section of your OpenAPI spec and reference it using $ref.

First, define your standard error model in components/schemas:

components:
  schemas:
    Error:
      type: object
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        detail:
          type

Then, reference this schema in your endpoint responses. This ensures all your errors are consistent.

paths:
  /users/{userId}:
    get:
      responses:
        '404':
          description: User not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal Server Error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

Use default error responses

OpenAPI also provides a default keyword for responses. This acts as a catch-all for any HTTP status codes that are not explicitly defined for an operation.

responses:
  '400':
    description: Bad Request
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/ValidationError'
  '404':
    description: Not Found
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/NotFoundError'
  default:
    description: Unexpected error
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/Error'

Error schema patterns and examples

Return validation errors

'422':
  description: Validation Error.
  content:
    application/json:
      schema:
        type: object
        properties:
          message:
            type: string
            example: 'The given data was invalid.'
          errors:
            type: array
            items:
              type: object
              properties:
                field:
                  type: string
                  example: 'billing_address.zip_code'
                message:
                  type: string
                  example: 'The zip code is not valid for the selected state.'

Return authentication errors

'401':
  description: Unauthorized.
  headers:
    WWW-Authenticate:
      schema:
        type: string
        example: 'Bearer'
  content:
    application/json:
      schema:
        $ref: '#/components/schemas/Error'

Return rate limit errors

'429':
  description: Too Many Requests.
  headers:
    Retry-After:
      description: The number of seconds to wait before making a new request.
      schema:
        type: integer
    X-RateLimit-Limit:
      description: The maximum number of requests you're permitted to make per hour.
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: The number of requests remaining in the current rate limit window.
      schema:
        type: integer
  content:
    application/json:
      schema:
        $ref: '#/components/schemas/Error'

Frequently asked questions about OpenAPI error schemas

Can I reuse one schema for several status codes?

Yes, and you absolutely should. Define the schema once under components/schemas and use $ref to point to it from multiple response definitions. This keeps your spec DRY and helps generators create clean, non-repetitive code.

How do error schemas influence generated SDKs?

They are fundamental. Good, consistent schemas allow a generator to create strongly-typed error classes, which enables features like specific exception handling, better IDE autocompletion, and a more predictable experience for your users.

Should I pick RFC 7807 or a custom format?

RFC 7807 is an excellent, well-supported standard that promotes interoperability. You should default to it unless you have a compelling reason and specific fields you need that don't fit its extension mechanism.

What happens to error schemas when generating MCP servers from OpenAPI specs?

The same schemas are often used to define the output of a tool when it fails. Simpler, flatter schemas tend to work better here, as they are easier for an LLM to understand and reason about.

What is the difference between default and explicit error responses?

Explicit responses (404, 422) document predictable, specific failures. The default response is a catch-all for unexpected errors, ensuring that even unknown failures can be mapped to a schema.

Ready to see how a well-defined OpenAPI spec can transform into a high-quality SDK? Get started for free.