Skip to content

Error Format — problem+json

The status code says which kind of error; the body should say what specifically went wrong, in a shape that is identical across every endpoint. Inventing a new error shape per endpoint is a tax clients pay forever.

There is a standard for exactly this: Problem Details for HTTP APIs (RFC 9457), served as application/problem+json. Its members:

  • type — a URI identifying the problem kind (a stable, documentable key).
  • title — a short, human-readable summary (stable per type).
  • status — the HTTP status code, repeated in the body.
  • detail — a human-readable explanation specific to this occurrence.
  • instance — a URI for this particular occurrence (often the request path).
  • plus any extension members you need (e.g. an errors array).
{
"type": "https://api.example.com/problems/out-of-stock",
"title": "Item is out of stock",
"status": 409,
"detail": "Only 2 units of SKU-123 remain; you requested 5.",
"instance": "/orders/8821",
"available": 2
}
JavaScript
Advantages (Standard Error Format)Trade-offs
Clients can handle errors programmaticallyRequires designing an error schema and sticking to it
A machine-readable error code drives retry logicToo much detail can leak internal information
The type URI in Problem Details points to documentationMore verbose than a plain { "error": "..." }
A stack trace in the error helps debugging in developmentA stack trace in production is a security risk

An error message that isn’t machine-readable Symptom:

  • { "error": "The email address you entered is already in use" }
  • The client has to parse a string to know the error type
  • Add a code field: { "code": "EMAIL_ALREADY_EXISTS", "message": "..." }

Exposing internal detail in a production error Symptom:

  • { "error": "Column 'users.email' cannot be null (SQL Error 1048)" }
  • This leaks the database schema to an attacker
  • Filter internal detail out in production: { "code": "VALIDATION_ERROR", "message": "Email is required" }

💡 Real-world examples

Stripe API:

  • Error shape: { "error": { "type": "card_error", "code": "card_declined", "message": "...", "param": "number" } }
  • type gives the category, code the specific error, param the field at fault

GitHub API:

  • Validation error: { "message": "Validation Failed", "errors": [{ "resource": "Issue", "field": "title", "code": "missing_field" }] }
  • A machine-readable code lets the client render the error message in its own language

Sources: Stripe API — errors, GitHub REST API errors

What media type does RFC 9457 use for error bodies?
What is the purpose of the `type` member?
When a problem needs no special category, `type` should be: