Knowledge

RFC 9457 for REST APIs: Problem Details as an error contract

How to standardize HTTP error payloads for better DX, observability, and diagnostics.

2/16/20268 min readKnowledge
RFC 9457 for REST APIs: Problem Details as an error contract

Executive summary

How to standardize HTTP error payloads for better DX, observability, and diagnostics.

Last updated: 2/16/2026

Introduction: The asymmetry of success and failure

Most APIs are meticulously designed for the happy path. Request schemas are validated, response models are typed, and success status codes are correct. But when things go wrong, the quality collapses. Error responses return raw strings, generic messages like "Something went wrong", or—worse—a 200 OK with an error field buried inside the JSON body.

This asymmetry is expensive. Without a structured, predictable error format, every API consumer must write bespoke error-handling logic. Support teams spend hours on incidents because error logs contain no actionable context. Observability dashboards can't aggregate failures by category because there is no standard taxonomy.

RFC 9457 (Problem Details for HTTP APIs) solves this by defining a machine-readable, standardized envelope for API errors. It turns failure from an afterthought into a first-class contract.

The RFC 9457 standard: What it defines

RFC 9457 specifies a JSON (or XML) response format with a well-defined set of fields. The content type is application/problem+json.

Core fields

json{
    "type": "https://api.example.com/errors/insufficient-funds",
    "title": "Insufficient Funds",
    "status": 422,
    "detail": "Account 12345 has a balance of $10.00, but the transaction requires $25.00.",
    "instance": "/transfers/txn-abc-123"
}
FieldPurposeKey Rule
typeA URI that uniquely identifies the category of the problem.Must be stable and dereferenceable (ideally links to documentation or a runbook). This is the primary field for automation.
titleA short, human-readable summary of the problem type.Should be the same for all occurrences of this type. Do not include instance-specific data here.
statusThe HTTP status code (repeated for convenience).Must match the actual HTTP status code of the response.
detailA human-readable explanation specific to this occurrence.This is where instance-specific context goes: account IDs, amounts, field names.
instanceA URI identifying the specific occurrence (for log correlation).Typically the request path or a unique trace/correlation ID.

Extension fields

RFC 9457 explicitly allows custom extension fields. This is where you add domain-specific context:

json{
    "type": "https://api.example.com/errors/validation-failed",
    "title": "Validation Failed",
    "status": 422,
    "detail": "The request body contains 2 validation errors.",
    "instance": "/users/signup",
    "errors": [
        { "field": "email", "message": "Must be a valid email address." },
        { "field": "password", "message": "Must be at least 8 characters." }
    ],
    "traceId": "abc-def-123-456"
}

The errors array and traceId are custom extensions—not defined by the RFC, but fully compliant.

Why this matters: Before and after

Without RFC 9457 (ad-hoc errors)

Different endpoints return errors in completely different formats:

json// Endpoint A
{ "error": "bad request" }

// Endpoint B
{ "code": 1001, "msg": "Invalid email" }

// Endpoint C
{ "success": false, "reason": "Not found" }

Every API consumer must write three different error-parsing strategies. Observability tools can't aggregate these. Support teams can't triage efficiently.

With RFC 9457 (standardized errors)

Every endpoint returns the same structure. Consumers write one error handler. Dashboards aggregate by type. Support teams link directly to runbooks from the type URI.

Deepening the analysis: Designing an error taxonomy

The most impactful architectural decision when adopting RFC 9457 is designing your error taxonomy—the catalog of type URIs.

Principles for a good taxonomy

  1. Domain-level, not code-level. Types should represent business-domain failures (insufficient-funds, order-already-shipped), not internal exceptions (NullPointerException, SequelizeValidationError).
  2. Stable and versioned. Once a type URI is published, changing its meaning is a breaking change for consumers who automate based on it.
  3. Never expose internal details. The detail field must be sanitized. Never leak stack traces, database table names, or internal service names in production error responses.
  4. Link to runbooks. If the type URI is dereferenceable (e.g., https://api.example.com/errors/rate-limit-exceeded), it should return a human-readable page explaining the error, possible causes, and resolution steps.

Example taxonomy structure

type URItitleTypical status
/errors/validation-failedValidation Failed422
/errors/resource-not-foundResource Not Found404
/errors/insufficient-fundsInsufficient Funds422
/errors/rate-limit-exceededRate Limit Exceeded429
/errors/conflict-duplicateDuplicate Resource Conflict409
/errors/unauthorizedAuthentication Required401
/errors/forbiddenInsufficient Permissions403
/errors/service-unavailableUpstream Service Unavailable503

When RFC 9457 accelerates delivery

Standardizing error contracts yields compounding returns across the organization:

  • Support cost reduction: Teams can automate triage based on the type field instead of parsing free-text messages.
  • Observability improvement: Dashboards can aggregate errors by type and status, enabling trend analysis (e.g., "validation errors increased 40% after the last deploy").
  • Consumer DX improvement: New API consumers learn one error format, write one error handler, and trust that it works consistently across all endpoints.

Decision prompts for your engineering context:

  • Which error classes in your domain need stable type identifiers for consumer automation (e.g., retry logic, user-facing messages)?
  • How will you version the error catalog without breaking existing clients?
  • Which correlation fields (traceId, requestId) are mandatory for support and audit workflows?

Tactical optimization plan

  1. Define a failure taxonomy by domain. Create a documented catalog of all problem types, grouped by business domain (Payments, Auth, Catalog).
  2. **Standardize application/problem+json globally.** Every error response across all API surfaces must use this content type.
  3. **Instrument metrics by type and endpoint.** Track which problem types occur most frequently and on which endpoints.
  4. Sanitize error payloads. Ensure no internal exception names, stack traces, SQL queries, or infrastructure details leak into production error responses.
  5. Document error examples in OpenAPI. Every endpoint's OpenAPI specification should include example application/problem+json responses for its expected failure modes.
  6. **Train consumers on type-based handling.** Consumers should branch their error-handling logic on the type field, not on string-matching the detail message.

Reliability validations

Measure the success of RFC 9457 adoption by tracking:

  • Mean diagnosis time per problem type: Has the time to diagnose issues decreased now that errors carry structured context?
  • RFC 9457 compliance rate: What percentage of error responses return a complete, valid application/problem+json envelope?
  • Support ticket quality: Have support tickets decreased or improved in quality now that consumers receive actionable technical context?

Want to convert this plan into measurable execution with lower technical risk? Talk to a web specialist with Imperialis to design, implement, and operate this evolution.

Sources

Related reading