application/problem+json
Problem detailsresponse body
application/problem+jsonA standard JSON shape for HTTP API errors, so clients can handle failures from any API the same way.
| MIME type | application/problem+json |
|---|---|
| Used for | Response body |
| Parameters | None |
| Specification | RFC 9457 |
What it looks like on the wire
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://example.com/problems/validation",
"title": "Your request has invalid fields.",
"status": 422,
"detail": "2 fields failed validation.",
"instance": "/orders/8f2c",
"errors": [
{ "field": "email", "message": "must be a valid email address" },
{ "field": "quantity", "message": "must be at least 1" }
]
}Content-Type: application/problem+json
Send it
Express
res.status(422).type("application/problem+json").json({
type: "https://example.com/problems/validation",
title: "Your request has invalid fields.",
status: 422,
detail: "2 fields failed validation.",
});Spring Boot
@ExceptionHandler(OutOfStockException.class)
ProblemDetail outOfStock(OutOfStockException e) {
ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT, e.getMessage());
pd.setType(URI.create("https://example.com/problems/out-of-stock"));
return pd;
}ASP.NET Core (minimal API)
builder.Services.AddProblemDetails();
app.MapPost("/orders", () =>
Results.Problem(title: "Out of stock", statusCode: StatusCodes.Status409Conflict));Read it in the client
fetch
const res = await fetch("/orders", { method: "POST", body });
if (!res.ok && res.headers.get("content-type")?.includes("problem+json")) {
const problem = await res.json();
showError(problem.title, problem.detail);
}Good to know
- RFC 9457 (2023) replaced RFC 7807. The format didn't change; the new RFC added a registry of common problem types.
typeis a URI that identifies the kind of problem. Clients should branch on it, not ontitle, which is for humans and may be translated. The default,about:blank, means the HTTP status says it all.- Add your own members, like
errorsabove, for details clients need. Clients must ignore members they don't recognize. - There's an XML variant,
application/problem+xml, but JSON is what nearly every API uses.
Useful here: 422 Unprocessable Content · 409 Conflict
Frequently asked questions
What fields does application/problem+json have?
Five standard members, all optional: type (a URI for the kind of problem, default about:blank), title (a short summary), status (the HTTP status code), detail (an explanation of this occurrence) and instance (a URI for this occurrence). You can add your own.
Should every API error use application/problem+json?
For JSON APIs it's a good default: clients need one parser for every error. Still send the right HTTP status code — the status member is advisory and should match it.
Is RFC 7807 still valid?
It's obsoleted by RFC 9457, which keeps the same format, so code written for RFC 7807 works unchanged.
Related types
Headers that go with it: Content-Type