RFC 9457 Problem Details Explained with JSON Examples
Understand RFC 9457 Problem Details for HTTP APIs with simple JSON examples, field explanations, best practices, and common API error response patterns.
RFC 9457 problem details explained with JSON examples
API errors are easy to make inconsistent.
One endpoint returns this:
{
"error": "Invalid email"
}
Another returns this:
{
"message": "User not found",
"code": "USER_NOT_FOUND"
}
A third returns an HTML error page because something broke behind a proxy.
That is painful for developers. It is also painful for API clients, SDKs, monitoring tools, and AI agents that need to understand what went wrong without guessing.
RFC 9457 gives HTTP APIs a standard way to describe errors. The format is called Problem Details for HTTP APIs. In JSON, it is usually sent with the application/problem+json content type.
This guide explains RFC 9457 in plain language and shows practical JSON examples for validation errors, authentication errors, not found errors, and internal server errors.
What is RFC 9457?
RFC 9457 is the current Problem Details standard for HTTP APIs. It defines a common response body format for API errors so clients can read the error in a predictable way.
A normal HTTP status code tells the client the broad category of failure. For example, 400 means the request was bad, 401 means authentication is required, and 404 means the resource was not found.
That is useful, but it is often not enough.
Problem Details JSON adds a structured body with fields such as:
typetitlestatusdetailinstance
A simple RFC 9457 response looks like this:
{
"type": "https://api.example.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The email field must contain a valid email address.",
"instance": "/requests/01HZY7N6F7Q4M9W8K2R3P5T6X1"
}
The goal is not to replace HTTP status codes. The goal is to add enough structured detail so humans and machines can handle the error properly.
RFC 9457 vs RFC 7807
RFC 9457 replaces RFC 7807. If you already know RFC 7807, the overall idea is the same: use a standard “problem details” object for HTTP API errors.
The practical difference is that RFC 9457 is the newer standard. It updates and clarifies parts of RFC 7807, including guidance around problem type URIs, multiple problems, and registered problem types.
| Topic | RFC 7807 | RFC 9457 |
|---|---|---|
| Status | Older standard | Current standard |
| Main purpose | Defines Problem Details for HTTP APIs | Defines the updated Problem Details standard |
| JSON media type | application/problem+json | application/problem+json |
| XML media type | application/problem+xml | application/problem+xml |
| Relationship | Original RFC | Obsoletes RFC 7807 |
For most API teams, the takeaway is simple: if you are starting today, reference RFC 9457.
Problem Details JSON structure
A Problem Details JSON response is a JSON object. It usually uses the application/problem+json media type.
Here is the common structure:
{
"type": "https://api.example.com/problems/example-problem",
"title": "Example problem",
"status": 400,
"detail": "A human-readable explanation of this specific error.",
"instance": "/requests/01HZY7N6F7Q4M9W8K2R3P5T6X1"
}
These fields are standard members. You can also add extension members when your API needs more specific data.
For example, validation errors often include an errors array:
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"detail": "One or more fields are invalid.",
"instance": "/requests/01HZY8K1V6FEY9B2G7X3J4A5C6",
"errors": [
{
"field": "email",
"message": "Enter a valid email address."
}
]
}
That errors field is not one of the five standard members. It is an extension member. That is allowed when the extension is useful and documented.
Standard RFC 9457 fields
type
The type field identifies the kind of problem.
Use a stable URI when possible:
{
"type": "https://api.example.com/problems/insufficient-balance"
}
The URI does not have to point to a working page, but it is better if it does. A documentation page can explain what the error means, why it happens, and how a developer can fix it.
A good type value should describe the problem category, not one single occurrence.
Good:
{
"type": "https://api.example.com/problems/validation-error"
}
Weak:
{
"type": "https://api.example.com/errors/request-928372"
}
The second example looks more like a request ID. That belongs in instance, not type.
title
The title field is a short human-readable summary of the problem type.
{
"title": "Validation failed"
}
Keep it stable. Do not put request-specific details in the title.
Good:
{
"title": "Payment method declined"
}
Weak:
{
"title": "Card ending in 4242 was declined at 10:42 AM"
}
The specific card and time belong in detail or a safe extension field.
status
The status field repeats the HTTP status code inside the JSON body.
{
"status": 404
}
The actual HTTP response status should still be correct:
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
And the body should match:
{
"status": 404
}
Do not send HTTP/1.1 200 OK with a Problem Details body that says "status": 404. That forces clients to choose between the real HTTP status and the JSON body.
detail
The detail field explains this specific occurrence of the problem.
{
"detail": "The email field must contain a valid email address."
}
Write detail for developers, but do not leak internals.
Good:
{
"detail": "The access token has expired. Request a new token and try again."
}
Risky:
{
"detail": "JWT verification failed in AuthService.validateToken() at line 284."
}
The second example exposes implementation details. Keep stack traces, database errors, file paths, and service internals out of public API responses.
instance
The instance field identifies this specific occurrence of the error.
{
"instance": "/requests/01HZY7N6F7Q4M9W8K2R3P5T6X1"
}
You can use a request path, trace URL, support reference, or another URI that helps your team find the error later.
A common pattern is to pair instance with a trace ID:
{
"instance": "/requests/01HZY7N6F7Q4M9W8K2R3P5T6X1",
"traceId": "01HZY7N6F7Q4M9W8K2R3P5T6X1"
}
If you expose trace IDs, make sure they do not reveal sensitive data.
Basic RFC 9457 JSON example
Here is a clean RFC 9457 example for a bad request.
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The request body is missing the required field: email.",
"instance": "/requests/01HZY9AQ7N0M6Q2V9S8K4T1B3C"
}
This response gives the client three useful layers of information:
| Layer | Example | Purpose |
|---|---|---|
| HTTP status | 400 Bad Request | Tells generic HTTP clients what happened |
| Problem type | https://api.example.com/problems/invalid-request | Tells API-aware clients the error category |
| Detail | The request body is missing the required field: email. | Tells the developer what to fix |
Use the REST API Error Response Format Checker to test whether your JSON follows a clean Problem Details-style structure.
Validation error example
Validation errors are one of the best uses for Problem Details JSON.
A single detail message is not always enough because several fields can be wrong at the same time. In that case, keep the standard fields and add an extension member for field-level errors.
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"detail": "The request body contains invalid fields.",
"instance": "/requests/01HZY9ZK3Y6R6SA1Z7BTM8P4KQ",
"errors": [
{
"field": "email",
"message": "Enter a valid email address."
},
{
"field": "password",
"message": "Password must be at least 12 characters."
}
]
}
This is easier to use than a single string like this:
{
"error": "Invalid email and password"
}
A frontend can map errors[0].field to the right form input. An SDK can expose field errors in a typed way. A developer can fix the request without reading API docs first.
You can also use JSON Pointer paths if your request body is nested:
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"detail": "The request body contains invalid fields.",
"errors": [
{
"pointer": "/customer/email",
"message": "Enter a valid email address."
},
{
"pointer": "/items/0/quantity",
"message": "Quantity must be greater than 0."
}
]
}
Pick one style and document it.
Authentication error example
Use 401 Unauthorized when the client needs valid authentication credentials.
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json
WWW-Authenticate: Bearer
{
"type": "https://api.example.com/problems/authentication-required",
"title": "Authentication required",
"status": 401,
"detail": "Send a valid Bearer token in the Authorization header.",
"instance": "/requests/01HZYAF5GN6JX2CH9B1S0X7F3D"
}
This is clear without exposing sensitive information. It tells the developer what the API expects, but it does not say whether a specific token exists, which account it belongs to, or how your authentication service failed internally.
For an expired token, you can use a more specific problem type:
{
"type": "https://api.example.com/problems/token-expired",
"title": "Token expired",
"status": 401,
"detail": "The access token has expired. Request a new token and try again.",
"instance": "/requests/01HZYAJ97XQ0S2M6K8R5C4V1BN"
}
Not found error example
Use 404 Not Found when the requested resource does not exist or should not be revealed to the caller.
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/resource-not-found",
"title": "Resource not found",
"status": 404,
"detail": "No project was found for the provided project ID.",
"instance": "/requests/01HZYB2FS9NA1D4W7CYQ8M0P6K"
}
Avoid messages that reveal too much.
Risky:
{
"detail": "Project 38291 exists, but it belongs to another organization."
}
Safer:
{
"detail": "No project was found for the provided project ID."
}
That distinction matters in multi-tenant APIs. A user should not be able to probe IDs and learn which resources exist in another account.
Internal server error example
Use 500 Internal Server Error when the server failed unexpectedly.
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/internal-server-error",
"title": "Internal server error",
"status": 500,
"detail": "The server could not complete the request. Try again later.",
"instance": "/requests/01HZYBKTTF8E7N2YQ4A6S9P0MX"
}
This is intentionally boring. A 500 response should not include stack traces, database names, hostnames, secret keys, SQL fragments, or internal service names.
For logs, use the instance or a trace ID to connect the public response to the private server-side error.
{
"type": "https://api.example.com/problems/internal-server-error",
"title": "Internal server error",
"status": 500,
"detail": "The server could not complete the request. Try again later.",
"instance": "/requests/01HZYBQ4KKA9K0V7V1CP5G3S8R",
"traceId": "01HZYBQ4KKA9K0V7V1CP5G3S8R"
}
Problem Details JSON schema
People often search for “problem details JSON schema” when they want to validate this format.
A practical schema for your API can start with the standard fields:
{
"type": "object",
"properties": {
"type": {
"type": "string",
"format": "uri-reference"
},
"title": {
"type": "string"
},
"status": {
"type": "integer",
"minimum": 100,
"maximum": 599
},
"detail": {
"type": "string"
},
"instance": {
"type": "string",
"format": "uri-reference"
}
}
}
For a real API, you will probably add stricter rules for your own problem types.
For example, your validation error schema might require an errors array:
{
"type": "object",
"required": ["type", "title", "status", "detail", "errors"],
"properties": {
"type": {
"const": "https://api.example.com/problems/validation-error"
},
"title": {
"const": "Validation failed"
},
"status": {
"const": 422
},
"detail": {
"type": "string"
},
"instance": {
"type": "string"
},
"errors": {
"type": "array",
"items": {
"type": "object",
"required": ["field", "message"],
"properties": {
"field": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
}
}
}
A schema can check the shape. It cannot check whether the error is helpful, safe, or mapped to the right HTTP status code. Review the wording too.
When to use RFC 9457
Use RFC 9457 when your API needs structured errors that clients can handle consistently.
It works well for:
- public REST APIs
- internal HTTP APIs used by several teams
- APIs with SDKs
- validation errors
- authentication and authorization errors
- rate limit errors
- payment or billing errors
- errors that need stable machine-readable codes
- systems where logs, traces, and API responses need to connect cleanly
RFC 9457 is especially helpful when different clients consume the same API. A web app, mobile app, backend service, and AI agent can all parse the same basic fields.
When not to overcomplicate your error format
Problem Details JSON is useful, but it does not mean every error needs a custom problem type.
Do not create a new problem type for every tiny variation.
Too much:
{
"type": "https://api.example.com/problems/user-email-field-empty-on-create-user-form-v2"
}
Better:
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"detail": "The request body contains invalid fields.",
"errors": [
{
"field": "email",
"message": "Email is required."
}
]
}
Also, do not use Problem Details as a public debug dump. The response should help the client fix or understand the HTTP-level problem. It should not explain your database schema, stack trace, deployment topology, or source code.
A good rule: if the message would make an attacker happier than a developer, do not put it in the response.
How to check RFC 9457-style responses with the tool
You can test an error body with the REST API Error Response Format Checker.
Paste your JSON response and check whether it has a clean Problem Details-style structure.
Use it to review:
- whether the response has standard fields like
type,title,status, anddetail - whether
statusmatches the HTTP status you plan to return - whether field-level validation errors are structured clearly
- whether the response is easy for frontend, backend, and SDK developers to parse
- whether the body avoids vague fields like
errorwith no extra context
For a broader guide to error response design, read REST API error response format best practices.
Best practices for RFC 9457 responses
Keep HTTP status codes correct
The JSON body should not fight the HTTP response.
Good:
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"status": 404
}
Bad:
HTTP/1.1 200 OK
Content-Type: application/problem+json
{
"status": 404
}
Clients, proxies, caches, monitoring tools, and SDKs expect the HTTP status to mean something.
Use stable problem type URIs
Your type URI should not change every time the wording changes.
Good:
{
"type": "https://api.example.com/problems/rate-limit-exceeded"
}
Weak:
{
"type": "https://api.example.com/problems/rate-limit-exceeded-v4-final-new"
}
If clients use type for logic, unstable values become breaking changes.
Put request-specific facts in detail or extensions
The title should stay short and stable. Put request-specific facts somewhere else.
Good:
{
"title": "Rate limit exceeded",
"detail": "You can make another request in 30 seconds.",
"retryAfterSeconds": 30
}
Weak:
{
"title": "Rate limit exceeded, try again in 30 seconds"
}
Document your extension members
Extension members are useful, but clients need to know what they mean.
If your API returns this:
{
"errors": [
{
"field": "email",
"message": "Enter a valid email address."
}
]
}
Document the errors array, the field format, and whether message is safe to show to end users.
Avoid sensitive details
Do not expose:
- stack traces
- SQL queries
- database table names
- internal service names
- file paths
- secrets
- full tokens
- private user data
- authorization rules that help attackers map your system
A safe error response should help legitimate developers without giving attackers a guide.
FAQ
What is RFC 9457 in simple terms?
RFC 9457 is a standard format for HTTP API error responses. It defines a Problem Details object that can include fields like type, title, status, detail, and instance.
What is an RFC 9457 example?
A basic RFC 9457 example looks like this:
{
"type": "https://api.example.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The request body is missing the required field: email.",
"instance": "/requests/01HZY9AQ7N0M6Q2V9S8K4T1B3C"
}
Send it with:
Content-Type: application/problem+json
What is Problem Details JSON?
Problem Details JSON is the JSON representation of the Problem Details format for HTTP APIs. It gives API errors a predictable structure instead of returning random error, message, or HTML responses.
What does application/problem+json mean?
application/problem+json is the media type used for JSON Problem Details responses. If someone says “application problem json”, they usually mean application/problem+json.
Is RFC 9457 only for REST APIs?
No. RFC 9457 is for HTTP APIs. It is commonly used in REST APIs because REST APIs rely on HTTP status codes and JSON response bodies, but the format is not limited to REST only.
Does RFC 9457 replace HTTP status codes?
No. RFC 9457 adds a structured response body. You should still send the correct HTTP status code in the response.
Should every API error use RFC 9457?
Not always. Use it when structured error details help clients. For very simple cases where the status code is enough, extra detail may not be needed.
Can I add custom fields to Problem Details JSON?
Yes. RFC 9457 allows extension members. Common examples include errors, traceId, code, retryAfterSeconds, or docs. Keep them documented and consistent.
What is the difference between type and instance?
type identifies the category of problem, such as validation-error. instance identifies this specific occurrence of the problem, such as a request ID or trace URI.
What is the best validation error format for RFC 9457?
A practical pattern is to use the standard Problem Details fields plus an errors array.
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"detail": "The request body contains invalid fields.",
"errors": [
{
"field": "email",
"message": "Enter a valid email address."
}
]
}
Conclusion
RFC 9457 gives API teams a clean default for error responses. Instead of inventing a new JSON shape for every project, you can use Problem Details JSON and make your errors easier to parse, log, test, and document.
Start with the standard fields:
{
"type": "https://api.example.com/problems/example-problem",
"title": "Example problem",
"status": 400,
"detail": "A clear explanation of what went wrong.",
"instance": "/requests/example-request-id"
}
Then add extension members only when they make the response easier to use.
Use the REST API Error Response Format Checker to test whether your JSON follows a clean Problem Details-style structure.