Understanding Problem JSON

Sander van Beek
4 min readSep 26, 2019

--

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:

--

--

Sander van Beek
Sander van Beek

Responses (2)