Standardize API Errors with RFC 9457 Problem Details

Implement RFC 9457 Problem Details with a governed type registry, safe extensions, useful remediation, consistent HTTP semantics, and a migration path for existing clients.

Edilec Research Updated 2026-07-13 Software Engineering

RFC 9457 Problem Details gives HTTP APIs a common container for machine-readable errors: application/problem+json. It does not create a universal catalog of business errors, replace HTTP status semantics, or authorize exposing stack traces. The standard succeeds when clients can classify a stable problem type, decide whether and how to remediate it, correlate one occurrence, and still rely on the actual HTTP response status. It fails when teams merely wrap every legacy message in five familiar fields.

The RFC 9457 specification defines type, status, title, detail, and instance, permits extension members, and obsoletes RFC 7807. Build an implementation around a small governed problem taxonomy and explicit client behavior. Keep transport status, domain classification, human explanation, and operational diagnostics separate. That separation allows a public error to be useful without becoming a debugging channel or a second, contradictory status protocol.

Design the six-stage problem-details lifecycle

Begin with failures clients must distinguish, not exception classes in server code. Group occurrences by remediation: correct a field, authenticate, request permission, wait and retry, resolve a state conflict, use a replacement resource, or contact support. Reuse an HTTP status alone when it expresses all needed semantics. Mint a problem type only when stable additional semantics improve automated or human recovery. This keeps the registry small enough for client teams to understand and test.

Six-stage RFC 9457 Problem Details diagram covering failure taxonomy, HTTP status, type registry, safe extensions, contract tests, and measured migration.
A problem response is dependable when HTTP semantics remain authoritative and every machine-actionable field has stable, safe meaning.

For each type, assign an owner and document the type URI, short title, usual HTTP status, applicability, extension schema, retry expectations, security classification, example, and lifecycle state. A type URI should remain stable and, where practical, resolve to human-readable documentation. Do not encode release numbers or transient implementation names in it. The type describes a class of problems; instance identifies a particular occurrence.

MemberPurposeGood practiceCommon mistake
typeStable problem classificationUse a controlled URI and document remediationPut an exception class or changing message in it
statusAdvisory copy of HTTP statusMatch the actual response statusUse it to contradict a gateway response
titleShort summary of the typeKeep stable except localizationInsert occurrence-specific values
detailHuman-readable occurrence explanationExplain correction without sensitive internalsRequire clients to parse it
instanceIdentifier for this occurrenceUse an opaque, safe correlation URI or identifierExpose internal hostnames or database keys
ExtensionsTyped machine-actionable contextDefine names, types, absence rules and sensitivityAdd arbitrary debug data per service

Make HTTP status and headers authoritative

Choose status codes according to HTTP semantics before choosing a problem type. Authentication failure, authorization refusal, absent resource, invalid representation, state conflict, rate limit, and temporary service failure have different implications for caches, intermediaries, monitoring, and generic clients. The status member is advisory; generators must send the same value in the actual response. A client should use the HTTP response status for protocol behavior and the problem type for application-specific handling.

Use established headers where they carry protocol information. Retry-After can communicate an appropriate retry time; authentication challenges belong in WWW-Authenticate; content negotiation still uses Content-Type and Accept. Do not duplicate every header in extension members. If a gateway rewrites the status, investigate the boundary rather than teaching clients that body and response status routinely disagree.

Define extensions as durable client contracts

Extensions should answer machine-actionable questions that the base members cannot. Validation failures might include a list of locations, rule identifiers, and safe messages. A quota problem might identify a quota class and reset policy. A state conflict might include the current version or permissible transition. Specify each extension's JSON type, requiredness, cardinality, ordering significance, localization behavior, and compatibility rules. Clients must tolerate unrecognized extensions so producers can add nonbreaking context.

Prefer identifiers over prose for automation. A client can switch on reason: creditlimitreached; it should not search detail for the word credit. Keep extension names portable and concise, and avoid nesting a complete legacy error envelope under error. That preserves the old model indefinitely and forces clients to understand two taxonomies. During migration, map each legacy code to one problem type and a limited set of typed extensions.

FailureHTTP statusProblem type decisionUseful extensionClient action
Malformed JSON400Usually one generic representation problemSafe parse location if availableCorrect request serialization
Field validation422 or API policy choiceStable validation typeArray of field paths and rule IDsCorrect listed values
Version conflict409State-conflict typeCurrent revision or ETagRefetch, merge and retry
Rate limit429Quota type if clients need domain detailQuota class; keep timing in headersWait according to policy
Unavailable dependency503Generic temporary failure may sufficeOptional safe dependency classRetry with backoff if operation is safe
Authorization policy403Type only when remediation differsRequired entitlement identifierRequest access or choose permitted action

Set a strict security and privacy boundary

Problem details are part of the public interface, not an observability payload. Never include stack traces, SQL, filesystem paths, secret values, internal service addresses, policy expressions, or the existence of resources a caller is not allowed to discover. Validation messages should not echo entire submitted values. Apply the same redaction rules to instance, extension fields, logs, and examples. A random occurrence identifier can link the caller to protected server-side diagnostics without exposing those diagnostics.

Threat-model differences between authenticated and anonymous callers. A login problem should not reveal whether an account exists. An object lookup may need the same outward response for absent and unauthorized resources. Rate-limit detail should help legitimate clients without disclosing defenses that enable evasion. Review new problem types as API changes, including abuse cases and tenant-boundary leakage, rather than letting each exception handler invent fields.

Separate machine behavior from localization

title can be localized, and detail is human-readable, but neither should drive client logic. If localization is offered, use normal content negotiation, state the supported languages, set response metadata correctly, and test fallbacks. Keep type URIs, extension keys, reason identifiers, field paths, and numeric values language-neutral. A support portal may render richer guidance from the type and locale while an SDK exposes typed remediation fields.

Write detail text for the API caller, not the server developer. Good detail explains what is wrong in this occurrence and what can be changed, without promising a retry will succeed when it may not. Avoid vague messages such as something went wrong. Also avoid embedding support playbooks in every response; use the type documentation or a safe support reference when the action requires human investigation.

Model and test Problem Details in OpenAPI

Define a reusable base schema with the standard members, then compose controlled schemas for problem types or extension families. Model URI references, integers, arrays, and nullable values accurately. Give operations the status responses they can actually produce, including gateway and authentication failures that bypass application code. Examples should be valid against the declared schema and free of real identifiers. Document application/problem+json explicitly rather than calling the response ordinary JSON.

Contract tests should verify content type, actual status, matching body status when present, stable type, required extensions, redaction, and unknown-extension tolerance. Add negative tests for accidental stack traces and oversized validation arrays. Client libraries should have a generic fallback for an unknown type and malformed or non-problem error bodies, because proxies and older services may still respond differently.

Migrate legacy error envelopes without stranding clients

Inventory legacy codes and observed client decisions before mapping. Some codes may be aliases; others may hide materially different remediation. Publish a mapping, add Problem Details behind content negotiation or a version boundary, and instrument which representation consumers request. Avoid returning both envelopes in one body. That hybrid becomes a permanent ambiguity about which code, message, and status is authoritative.

Move clients first where possible: teach them to parse Problem Details, tolerate unknown types, and fall back safely. Then migrate producers by domain and monitor unknown-type rates, parsing failures, retry behavior, and support volume. Retire legacy formats only when the consumer inventory and traffic evidence support it. Keep the type registry independently versioned so documentation and ownership survive service reorganizations.

Key takeaways

  • Create problem types from stable remediation semantics, not server exception classes.
  • Keep the actual HTTP status authoritative and use established headers for protocol behavior.
  • Make extensions typed, documented, optional where possible, and safe for unknown-field handling.
  • Never expose internal diagnostics or resource existence through public problem responses.
  • Keep identifiers machine-stable and human text localizable.
  • Migrate with a code-to-type map, client fallback, telemetry, and a dated legacy retirement plan.

RFC 9457 Problem Details FAQ

Does every HTTP error need a custom problem type?

No. If the HTTP status and safe detail fully explain the failure, about:blank or a simple response may be sufficient. Add a type when stable additional semantics enable distinct remediation or cross-service consistency.

May a client parse the detail string?

It should not. Detail is human-readable and may change or be localized. Put machine decisions in the type URI and documented extension members, while clients retain an unknown-type fallback.

What if the body status differs from the response status?

Treat the HTTP response status as authoritative and record the mismatch as a producer or intermediary defect. RFC 9457 requires generators to align them; designing routine disagreement into clients conceals an operational problem.

Conclusion

RFC 9457 standardizes the shape of an API problem, while teams remain responsible for useful semantics. A small registry, correct HTTP behavior, safe typed extensions, clear remediation, contract tests, and measured migration create errors that clients can handle consistently. The result is not merely uniform JSON; it is a dependable failure interface that remains understandable across services and releases.

Continue with related articles

Error Handling: Implementation Checklist

Implement error handling that helps users act, protects sensitive details, preserves diagnostic evidence, and gives operations a clear route to recovery.

Software Engineering · 12 min

Error Handling That Gives Teams a Safe Next Step

A practical error handling guide for engineering teams: classify failures by recovery, give each boundary a stable contract, protect diagnostics, and improve from evidence.

Software Engineering · 13 min read

Node.js APIs Before Build: Contracts and Recovery

Design Node.js APIs around explicit contracts, server-side authority, durable asynchronous work, safe errors, and request-to-outcome evidence before implementation begins.

Software Engineering · 13 min