I could not find a good human-readable resource about Problem. Therefore I’m writing it myself.
What is Problem JSON
Problem is a standardized way of describing any kind of error thrown by an API. A Problem can be described in JSON or XML, but we’re only going to talk about the JSON variant in this article.
The purpose of Problem is so that “API [consumers] can be informed of both the high-level error class (using the status code) and the finer-grained details of the problem”.
For those of you who don’t like reading, here is an example Problem JSON response:
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json; charset=utf-8
Date: Wed, 07 Aug 2019 10:10:06 GMT
{
"type": "https://example.com/probs/cant-view-account-details",
"title": "Not authorized to view account details",
"status": 401,
"detail": "Due to privacy concerns you are not allowed to view account details of others. Only users with the role administrator are allowed to do this.",
"instance": "/account/123456/details"
}
Examples
I like to learn with examples, so I’ll include a bunch.
Normal HTTP status code
When you can’t or don’t want to provide any details.
{
"title": "Unauthorized",
"status": 401
}
title
should equal the description of the HTTP status code as defined here.
Adding some details for recreating the problem
{
"title": "Unauthorized",
"status": 401,
"instance": "/login/someUser"
}
Custom members are allowed
Note that balance
is not a default member of Problem.
{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit",
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc",
"balance": 30
}
{
"title": "Forbidden",
"status": 403,
"balance": 30
}
Content-type
The HTTP header Content-Type
for a Problem response must be application/problem+json
. This makes Problem easily identified by the consumer.
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Date: Wed, 07 Aug 2019 10:10:06 GMT
Default members
Problem is defined as a collection of members, each with a very specific purpose. A producer should include as many default members as possible.
type
{URI}
A URL to a page with more details regarding the problem.
"type": "https://example.com/probs/cant-view-account-details",
The primary identifier for the problem.
type
is typically an absolute URL that leads to an HTML page containing human-readable documentation regarding the problem.
When type
is not provided (undefined
) the consumers must assume that type equals about:blank
.
The URI in thetype
field should not be opened automatically.
title
{string}
Short human-readable summary of the problem.
"title": "Not authorized to view account details",
When type
equals about:blank
then title
should equal the description of the HTTP status code:
{
"type": "about:blank",
"title": "Not found",
"status": 404,
}
If type
is specified then title
should not equal the description of the HTTP status code:
{
"type": "https://example.com/probs/no-account-details",
"title": "No account details found",
"status": 404,
}
title
should not change from occurrence to occurrence of the problem, so no timestamps/counters/etc. The exception to this is localization. title
can differ for the same problem if the locale is different.
title
should not be parsed by a machine, it is only for meant for humans. A machine should use type
instead.
status
{number}
The HTTP status code.”status”: 401,
status
is always the same as the status code in the HTTP header.
status
is only included for the convenience of the consumer.
detail
{string}
Human-readable description of this specific problem.
"detail": "Due to privacy concerns you are not allowed to view account details of others. Only users with the role administrator are allowed to do this.",
detail
should help the consumer to correct the problem and not only provide debugging information.
detail
should not be parsed by a machine, it is only for meant for humans.
instance
{URI}
A URI that describes where the problem occurred.
"instance": "/account/123456/details"
instance
can be an absolute or relative path.
instance
does not have to lead to anything like type
does. It is used to recreate the problem later.
Custom members
Problem may be extended with custom members to convey problem-specific information. All members that are not recognized by the client must be ignored. Never throw an error due to an unrecognized member.
There are no restrictions for custom members.
This allows producers to extend Problem with highly detailed information about a problem and it allows Problem to evolve without losing backwards compatibility.
Note that invalid-params
is not a default member:
{
"title": "Bad request",
"status": 400,
"invalid-params": [
{
"name": "age",
"reason": "must be a positive integer"
},
{
"name": "color",
"reason": "must be 'green', 'red' or 'blue'"
}
]
}
Conclusion
Problem is a push towards standardizing error messages in APIs. It has plenty of flexibility and extendibility to the point I can’t think of any situation where you shouldn’t use it.
I hope I’ve provided enough examples to make Problem clear without you having to read the RFC.
Sources
Only the latest RFC for Problem was used as a source: