On this page · 13 sections
- The shape of a problem detail
- Each field, and where teams get it wrong
- Extension members
- Handling multiple problems
- Custom problem types and the IANA registry
- RFC 9457 versus RFC 7807
- Framework support in 2026
- Security considerations
- Consuming problem details on the client
- India-specific considerations
- FAQ
- How eCorpIT can help
- References
Summary. RFC 9457, "Problem Details for HTTP APIs," is the IETF standard for returning machine-readable errors from an HTTP API. Published in 2023, it obsoletes RFC 7807 with no breaking changes, and it carries five members: type, title, status, detail, and instance, served as application/problem+json. The 2026 relevance is adoption: Spring Framework 6.0.0 added a ProblemDetail type, Spring Boot 4 makes it the framework-wide default, and ASP.NET Core 7 and later ship it natively, so a compliant error response costs almost nothing to produce. RFC 9457 adds three things over RFC 7807: guidance for reporting multiple problems, an IANA registry of common problem types, and explicit rules for extension members. This guide shows the format, the security traps, and how to adopt it.
Most APIs still return errors as an ad hoc {"error": "..."} blob, and every service invents its own shape. That is the exact problem the standard names. As the RFC 9457 specification puts it, its aim is "to define common error formats for applications that need one so that they aren't required to define their own or, worse, tempted to redefine the semantics of existing HTTP status codes." The standard was authored for the IETF by Mark Nottingham, Erik Wilde, and Sanjay Dalal, and it builds on RFC 7807, whose journey started in 2016.
A quick clarification before the format: problem details do not replace HTTP status codes, and they are not a debugging tool for your implementation. A 404 is still a 404. The problem detail adds the human-readable and machine-readable context that a bare status code cannot carry. If you are also tracking new HTTP mechanics this cycle, the same working group standardised the QUERY method in RFC 10008.
The shape of a problem detail
A problem detail is a JSON object. Here is the canonical example from the specification, a 403 for an account that ran out of credit:
{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"status": 403,
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc"
}
The full HTTP exchange sets the media type and language:
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"status": 403,
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc"
}
Two media types are registered: application/problem+json and application/problem+xml. The five members break down as follows.
| Member | What it holds | Notes |
|---|---|---|
type |
A URI naming the problem category | Defaults to about:blank if absent; a locator URL should point to human-readable docs |
title |
A short, human-readable summary | Should stay constant per type, except for localisation |
status |
The HTTP status code | Informative duplicate of the response code |
detail |
An explanation of this occurrence | Keep it human-readable; no stack traces |
instance |
A URI for the specific occurrence | Can be a dereferenceable locator or an opaque tag |
All five members are optional. If you omit type, it is treated as about:blank, which means "the problem is fully described by the status code." That gives you a compliant minimal response when you only want to restate the status in the standard format.
Each field, and where teams get it wrong
The type field is the anchor. It is a URI that identifies the category of problem, and clients use it to branch logic. If the URI is a locator, dereferencing it should return documentation for developers, though clients should not fetch it automatically. Use absolute URIs, not relative ones, to avoid ambiguity.
The status field duplicates the HTTP status code inside the body. Its purpose is to let a consumer see the origin server's intended code even if an intermediary rewrote the response line. Because it is a duplicate, keep it consistent with the real status; a mismatch is a genuine source of bugs and can be abused if set carelessly.
The title and detail fields split the human-readable message in two. The title is stable per problem type, like a constant label. The detail explains this specific occurrence. The most important rule here is a security rule: the detail should not carry stack traces, SQL fragments, or internal identifiers, because an error response is an information-disclosure surface. Put anything sensitive or diagnostic into an extension member you control and can strip in production.
The instance field is a URI that names this particular occurrence. It can be a dereferenceable locator that returns the problem object, or an opaque tag that only the server understands. It is useful for log correlation.
Extension members
The five standard fields are rarely enough. RFC 9457 lets you add extension members, any additional JSON fields that carry more context. The one hard rule for clients is that unrecognised extensions must be ignored, never a reason to reject the response. That is what keeps the format forward-compatible: a client written against last year's schema still parses this year's richer error.
{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"status": 403,
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc",
"balance": 30,
"accounts": ["/account/12345", "/account/67890"]
}
Here balance and accounts are extensions. A client that knows about them can render a helpful message; one that does not simply skips them.
Handling multiple problems
Validation is the case where one error is never enough. A form with a bad age and an invalid colour has two problems at once. RFC 9457 gives clear guidance that RFC 7807 lacked. Return a single problem detail scoped to one resource type, and carry the individual issues in an extension member, typically an errors array where each entry has a detail and a JSON Pointer into the request:
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
Content-Language: en
{
"type": "https://example.net/validation-error",
"title": "Your request is invalid.",
"errors": [
{ "detail": "must be a positive integer", "pointer": "#/age" },
{ "detail": "must be 'green', 'red' or 'blue'", "pointer": "#/profile/color" }
]
}
The JSON Pointers (#/age, #/profile/color) tell the client exactly which fields failed. Two design rules follow. Do not mix unrelated problem types in one response; if several distinct problems occur, report the most urgent or relevant one. And RFC 9457 explicitly steps away from the old idea of using HTTP 207 Multi-Status to batch errors, because that reopens multi-request and multi-response ambiguity that HTTP does not model natively.
Custom problem types and the IANA registry
You will define your own problem types. A good custom type documents three things: a URI for the type, preferably a dereferenceable locator that explains how to resolve the problem; a short title; and the HTTP status code it maps to. Custom types can also use the Retry-After header where it fits.
The change that matters for the wider ecosystem is the registry. RFC 9457 adds an IANA registry of HTTP problem types, where common categories can be registered through a template and expert review. The point is reuse: instead of every API inventing a slightly different "rate limited" type, teams can converge on registered ones, which makes cross-API tooling and client libraries more useful. For teams moving to distributed systems, this consistency pays off directly, as covered in our guide to monolith-to-microservices modernization.
RFC 9457 versus RFC 7807
If you already return RFC 7807 responses, there is almost nothing to change. RFC 9457 obsoletes RFC 7807 and is fully backward compatible.
| Area | RFC 7807 | RFC 9457 |
|---|---|---|
| Status | Original spec, from 2016 | Obsoletes 7807, published 2023 |
| Multiple problems | Suggested HTTP 207 Multi-Status | Extension member plus JSON Pointers; report the priority problem |
| Problem type registry | None | IANA registry of common types |
| Extension members | Allowed, lightly specified | Explicitly allowed and clarified |
| Type-to-fields link | Loose | Clearer association between a type and its extra fields |
| Backward compatible | Baseline | Yes, no breaking changes |
The takeaway is that RFC 9457 is a refinement, not a rewrite. Point your documentation at the new number, adopt the multiple-problems and registry guidance for new work, and existing responses keep working.
Framework support in 2026
Adoption is the reason this is a 2026 topic rather than a 2023 one. The major backend frameworks now produce RFC 9457 responses out of the box.
| Framework | RFC 9457 support | Since |
|---|---|---|
| Spring Framework | ProblemDetail representation |
6.0.0 |
| Spring Boot | Framework-wide default in Boot 4; opt-in in Boot 3 | 3.x, default in 4 |
| ASP.NET Core | Native ProblemDetails |
7.0 and later |
| Quarkus | Community and built-in support | 2024 onward |
| Java (Zalando problem) | Mature library | Pre-dates 9457, 7807-compatible |
In Spring Boot 3 you switch it on with spring.mvc.problemdetails.enabled=true; Spring Boot 4 makes it the default so you no longer opt in per controller. In ASP.NET Core you return the built-in ProblemDetails type or let the framework produce it for unhandled errors. Either way, a compliant response is close to free, which is what makes standardising worthwhile across a fleet of services. That is a natural fit inside a broader platform engineering practice, where consistent contracts reduce integration cost.
Security considerations
Two of the fields deserve a security pass. The detail field is the common leak: teams pipe exception messages straight into it, exposing internals to anyone who can trigger an error. Keep detail human-readable and generic, and move diagnostic data into extension members that you redact outside development. The status field, being a duplicate of the response code, should never contradict it. Beyond the fields, remember that a rich, consistent error format is easier for attackers to probe too, so rate-limit error-heavy endpoints and avoid echoing user input verbatim.
Consuming problem details on the client
A standard error format is only half the value; the other half is a client that reads it. Because the response is JSON with a known media type, a consumer can branch on type and fall back to status when it does not recognise the type. A minimal handler in TypeScript looks like this:
async function callApi(url, body) {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, application/problem+json' },
body: JSON.stringify(body),
});
if (res.ok) return res.json();
const contentType = res.headers.get('Content-Type') || '';
if (contentType.includes('application/problem+json')) {
const problem = await res.json();
// Branch on the stable problem type, not the human-readable title
switch (problem.type) {
case 'https://example.com/probs/out-of-credit':
return handleOutOfCredit(problem);
default:
return showGenericError(problem.title, problem.status);
}
}
throw new Error(`Unexpected error: ${res.status}`);
}
Three habits separate a resilient client from a fragile one. Branch on type, which is stable, not on title, which can be localised or reworded. Read extension members defensively, treating any you do not recognise as optional. And send an Accept header that lists application/problem+json, so a content-negotiating server knows you can parse the format.
The common mistakes mirror those habits. Parsing detail with a regular expression to extract values is brittle; put structured data in extension members and read those instead. Assuming every field is present breaks clients, because all five members are optional. And dereferencing the type URI automatically on every error wastes requests; fetch it only when a developer needs the documentation.
India-specific considerations
For Indian teams building APIs for regulated sectors, the detail rule intersects with the DPDP Act. An error message that echoes a user's personal data, or a database record, into detail can become an unintended disclosure. Treat problem details as part of your data-handling review: keep personal data out of error bodies, log the correlating instance identifier instead, and standardise the redaction in a shared error handler rather than per endpoint. For consultancies delivering to multiple clients, a single RFC 9457 error module reused across projects is both cheaper to maintain and easier to audit.
FAQ
How eCorpIT can help
eCorpIT designs and modernises HTTP APIs for clients in India and abroad, and we standardise error handling as part of that work. If your team wants an RFC 9457 error module, a review of what your API leaks in error bodies, or a migration plan across a set of microservices, our senior engineering team can help scope it. Reach us through the contact page.
References
- IETF, RFC 9457: Problem Details for HTTP APIs, rfc-editor.org, 2023.
- IETF, RFC 9457 datatracker record, datatracker.ietf.org.
- IANA, HTTP Problem Types registry, iana.org.
- Swagger, Problem Details (RFC 9457): Doing API Errors Well, swagger.io.
- Swagger, Problem Details (RFC 9457): Getting Hands-On, swagger.io.
- Stefano Fago, RFC 7807 is dead, long live RFC 9457, A Java geek, 2023.
- Spring, Error Responses, docs.spring.io.
- Milan Jovanović, Problem Details for ASP.NET Core APIs, milanjovanovic.tech.
- Redocly, RFC 9457: Better information for bad situations, redocly.com.
- codecentric, Deep dive into RFC 7807 and RFC 9457, codecentric.de.
- IETF, RFC 7807 (previous specification), datatracker.ietf.org.
- Harshal Ahire, Spring Boot 4: Standardizing Microservice Error Responses with RFC 9457, Medium, April 2026.
_Last updated: July 7, 2026._