RFC 9457 Standard Error Format for APIs

Stop inventing custom error formats. RFC 9457 Problem Details provides a standard way to return HTTP API errors with machine-readable context.

TRY NANO BANANA FOR FREE

RFC 9457 Standard Error Format for APIs

TRY NANO BANANA FOR FREE
Contents

Your API returns errors like this:

{
  "error": "Something went wrong",
  "code": "ERR_001"
}

Or maybe like this:

{
  "success": false,
  "message": "Validation failed",
  "errors": ["Name is required"]
}

Or perhaps like this:

{
  "status": "error",
  "data": null,
  "errorMessage": "Invalid request"
}

Every API invents its own error format. Clients must parse different structures for different APIs. There's no consistency.

RFC 9457 solves this. It defines a standard format for HTTP API errors that's machine-readable, extensible, and widely supported.

What Is RFC 9457?

RFC 9457 (Problem Details for HTTP APIs) is an IETF standard published in July 2023. It defines a JSON (and XML) format for describing errors in HTTP APIs.

The format includes:

  • type: A URI identifying the error type
  • title: A short, human-readable summary
  • status: The HTTP status code
  • detail: A human-readable explanation specific to this occurrence
  • instance: A URI identifying the specific occurrence

Here's a basic example:

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The request body contains validation errors",
  "instance": "/v1/pets"
}

This format is consistent, self-documenting, and machine-readable. Clients can parse it reliably across different APIs.

Why Use RFC 9457?

Reason 1: Standardization

When you use RFC 9457, clients know what to expect. They can write generic error handling code that works across multiple APIs.

Without a standard, every API is different:

// Custom error format A
if (response.error) {
  console.error(response.error.message);
}

// Custom error format B
if (!response.success) {
  console.error(response.errorMessage);
}

// Custom error format C
if (response.status === 'error') {
  console.error(response.data.error);
}

With RFC 9457, error handling is consistent:

if (response.status >= 400) {
  const problem = await response.json();
  console.error(`${problem.title}: ${problem.detail}`);

  // Machine-readable error type
  if (problem.type === 'https://petstoreapi.com/errors/validation-error') {
    // Handle validation errors specifically
  }
}

Reason 2: Machine-Readable Error Types

The type field is a URI that uniquely identifies the error. This enables programmatic error handling.

{
  "type": "https://petstoreapi.com/errors/rate-limit-exceeded",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "You have exceeded the rate limit of 1000 requests per hour",
  "retryAfter": 3600
}

Clients can check the type and handle specific errors:

if (problem.type === 'https://petstoreapi.com/errors/rate-limit-exceeded') {
  const retryAfter = problem.retryAfter || 60;
  console.log(`Rate limited. Retry after ${retryAfter} seconds`);
  setTimeout(() => retryRequest(), retryAfter * 1000);
}

Reason 3: Extensibility

RFC 9457 allows custom fields. You can add context-specific information while maintaining the standard structure.

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The request body contains validation errors",
  "instance": "/v1/pets",
  "errors": [
    {
      "field": "name",
      "message": "Name is required",
      "code": "REQUIRED_FIELD"
    },
    {
      "field": "age",
      "message": "Age must be between 0 and 30",
      "code": "OUT_OF_RANGE"
    }
  ]
}

The standard fields (type, title, status, detail, instance) are always present. Custom fields (errors) provide additional context.

Reason 4: Tool Support

Many tools and libraries support RFC 9457:

  • API gateways can transform errors into Problem Details format
  • Client libraries can parse Problem Details automatically
  • Monitoring tools can extract structured error information
  • Documentation tools can generate error documentation from Problem Details schemas

Using a standard format makes your API easier to integrate with existing tools.

The Five Standard Fields

type (URI)

A URI that identifies the error type. This should be a stable identifier that doesn't change.

{
  "type": "https://petstoreapi.com/errors/not-found"
}

The URI doesn't need to resolve to a real page, but it can. Many APIs host error documentation at these URLs:

https://petstoreapi.com/errors/not-found
→ Returns HTML documentation about 404 errors

If you don't provide a type, it defaults to about:blank, which means "the error is explained by the HTTP status code alone."

title (String)

A short, human-readable summary of the error type. This should be the same for all occurrences of this error type.

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error"
}

The title is generic. It describes the error type, not the specific occurrence.

status (Integer)

The HTTP status code. This duplicates the HTTP response status but makes the error self-contained.

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422
}

Including status in the body is useful when errors are logged, queued, or stored. You don't need to track the HTTP status separately.

detail (String)

A human-readable explanation specific to this occurrence of the error.

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The 'name' field is required but was not provided"
}

The detail is specific. It explains what went wrong in this particular request.

instance (URI)

A URI identifying the specific occurrence of the error. This is often the request path.

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The 'name' field is required but was not provided",
  "instance": "/v1/pets"
}

The instance helps with debugging. When you see an error in logs, you know which endpoint caused it.

Common Error Patterns

Validation Errors

Validation errors need field-level details. Add a custom errors array:

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The request body contains 2 validation errors",
  "instance": "/v1/pets",
  "errors": [
    {
      "field": "name",
      "message": "Name is required",
      "code": "REQUIRED_FIELD"
    },
    {
      "field": "age",
      "message": "Age must be a positive integer",
      "code": "INVALID_TYPE"
    }
  ]
}

Clients can iterate over errors and display field-specific messages.

Not Found Errors

404 errors are straightforward:

{
  "type": "https://petstoreapi.com/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "Pet with ID '019b4999-9999-9999-9999-999999999999' not found",
  "instance": "/v1/pets/019b4999-9999-9999-9999-999999999999"
}

The detail includes the specific ID that wasn't found.

Authentication Errors

401 errors indicate missing or invalid authentication:

{
  "type": "https://petstoreapi.com/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Access token is missing or invalid",
  "instance": "/v1/pets/019b4132-70aa-764f-b315-e2803d882a24"
}

Include the WWW-Authenticate header to tell clients how to authenticate:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="PetStore API"
Content-Type: application/problem+json

Authorization Errors

403 errors indicate insufficient permissions:

{
  "type": "https://petstoreapi.com/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "You don't have permission to delete this pet",
  "instance": "/v1/pets/019b4132-70aa-764f-b315-e2803d882a24",
  "requiredScope": "write:pets"
}

The custom requiredScope field tells clients what permission they need.

Rate Limit Errors

429 errors indicate rate limiting:

{
  "type": "https://petstoreapi.com/errors/rate-limit-exceeded",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "You have exceeded the rate limit of 1000 requests per hour",
  "instance": "/v1/pets",
  "limit": 1000,
  "remaining": 0,
  "resetAt": "2026-03-13T12:00:00Z"
}

Include rate limit details so clients know when they can retry.

Server Errors

500 errors indicate server failures:

{
  "type": "https://petstoreapi.com/errors/internal-server-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred while processing your request",
  "instance": "/v1/pets",
  "traceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Include a traceId for debugging. Don't expose internal error details to clients.

Implementation Tips

Set the Content-Type

Use application/problem+json as the content type:

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "https://petstoreapi.com/errors/validation-error",
  ...
}

This tells clients the response follows RFC 9457.

Use Consistent Error Types

Define your error types upfront and document them:

https://petstoreapi.com/errors/validation-error
https://petstoreapi.com/errors/not-found
https://petstoreapi.com/errors/unauthorized
https://petstoreapi.com/errors/forbidden
https://petstoreapi.com/errors/rate-limit-exceeded
https://petstoreapi.com/errors/internal-server-error

Don't create new error types for every situation. Reuse existing types and vary the detail field.

Document Your Error Types

Host documentation at your error type URLs:

GET https://petstoreapi.com/errors/validation-error

Returns:
<!DOCTYPE html>
<html>
<head><title>Validation Error</title></head>
<body>
  <h1>Validation Error (422)</h1>
  <p>This error occurs when the request body fails validation...</p>
</body>
</html>

This makes your API self-documenting.

Include Helpful Context

Add custom fields that help clients handle errors:

{
  "type": "https://petstoreapi.com/errors/payment-failed",
  "title": "Payment Failed",
  "status": 402,
  "detail": "The payment method was declined",
  "instance": "/v1/orders/019b4132-70aa-764f-b315-e2803d882a24/payment",
  "paymentMethod": "card_1234",
  "declineCode": "insufficient_funds",
  "retryable": true
}

The retryable field tells clients whether they should retry.

Don't Expose Internal Details

Never include stack traces, database queries, or internal paths in error responses:

Bad:

{
  "type": "https://petstoreapi.com/errors/internal-server-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "NullPointerException at com.example.PetService.getPet(PetService.java:42)",
  "stackTrace": "..."
}

Good:

{
  "type": "https://petstoreapi.com/errors/internal-server-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred. Please contact support with trace ID a1b2c3d4",
  "traceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Log internal details server-side. Return only safe information to clients.

Real-World Example: Modern PetStore API

The Modern PetStore API uses RFC 9457 throughout. Here's a validation error from creating a pet:

POST /v1/pets
Content-Type: application/json

{
  "name": "",
  "species": "INVALID",
  "age": -5
}

Response:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The request body contains 3 validation errors",
  "instance": "/v1/pets",
  "errors": [
    {
      "field": "name",
      "message": "Name cannot be empty",
      "code": "EMPTY_STRING"
    },
    {
      "field": "species",
      "message": "Species must be one of: DOG, CAT, BIRD, RABBIT, REPTILE, FISH, OTHER",
      "code": "INVALID_ENUM"
    },
    {
      "field": "age",
      "message": "Age must be between 0 and 30",
      "code": "OUT_OF_RANGE"
    }
  ]
}

The error is structured, machine-readable, and includes all the context needed to fix the request.

Start Using RFC 9457 Today

Implementing RFC 9457 is straightforward:

  1. Define your error types (validation-error, not-found, unauthorized, etc.)
  2. Create a Problem Details response structure with the five standard fields
  3. Add custom fields for context-specific information
  4. Set Content-Type to application/problem+json
  5. Document your error types at their URIs

Your API will be more consistent, easier to debug, and better integrated with modern tools.

Stop inventing custom error formats. Use the standard.

Try It Yourself: The Modern PetStore API uses RFC 9457 for all errors. Explore the live API at petstoreapi