Error Handling
As per HTTP specifications, the outcome of a request execution could be specified using an integer and a message. The number is known as the status code and the message as the reason phrase. The reason phrase is a human-readable message used to clarify the outcome of the response. The HTTP status codes in the 4xx range indicate client-side errors (validation or logic errors), while those in the 5xx range indicate server-side errors (usually defect or outage). However, these status codes and human-readable reason phrases are not sufficient to convey enough information about an error in a machine-readable manner. To resolve an error, non-human consumers of RESTful APIs need additional help.
Therefore, APIs MUST return a JSON error representation that conforms to the schema defined in RFC 7807. Zalando have implemented a JSON Schema definition of the RFC 7807 spec which we use by reference.
Error Schema
An error response following RFC 7807 schema MUST conform to the following structure:
{
"type": "https://api.example.com/types/<namespace>/errors/<category>/<error>",
"title": "Request data validation error",
"status": 400,
"detail": "One or more fields failed validation",
"instance": "UUID",
"invalidParams": [{
"field": "nickname",
"code": "https://api.example.com/types/<namespace>/errors/validations/missing",
"message": "The value for this field needs to be present."
}]
}
The error response structure includes the following fields:
Field | Description |
---|---|
type | A human-readable, unique URI for the error. The URI MAY be an endpoint which provides more information about the type of error. |
title | A human-readable message, describing the error. This message MUST be a description of the problem NOT a suggestion about how to fix it. |
status | The HTTP Error Code response of this error. |
detail | If present, the detail field SHOULD focus on helping the client correct the problem, rather than giving debugging information. |
instance | A unique error identifier generated on the server-side and logged for correlation purposes. This could be trace / span identifiers. |
invalidParams | An extension to the error payload. An array that contains individual instance(s) of the error with specifics such as the following. This field is required for client side errors (4xx). |
field | JSON Pointer to the field in error if in body, else name of the path parameter or query parameter. |
code | A human-readable, unique name for the error. |
message | A human-readable message, describing the error. This message MUST be a description of the problem NOT a suggestion about how to fix it. |
errors | An array of individual request errors (or successes) when a bulk request fails. |
NOTE: The domain name for the error types should be reflective of your organization, not
example.com
. In this instance, it should betwdps.io
.
Use of JSON Pointer
If you have used some other means to identify the field in an already released API, you could continue using your existing approach. However, if you plan to migrate to the approach suggested, you would want to bump up the major version of your API and provide migration assistance to your clients as this could be a potential breaking change for them. The JSON Pointer for the field SHOULD be a JSON string value.
Input Validation Errors
In validating requests, there are a variety of concerns that should be addressed in the following order:
Problem | Code |
---|---|
Not well-formed JSON | 400 Bad Request |
Contains validation errors that the client can change. | 400 Bad Request |
Cannot be executed due to factors outside of the request body. | 422 Unprocessable Entity |
The request was well-formed but was unable to be followed due to semantic errors. | 409 Conflict |
Error Samples
This section provides some samples to describe usage of RFC 7807 in various scenarios.
Validation Error Response - Single Field
The following sample shows a validation error in one field.
Because this is a client error, a 400 Bad Request
HTTP status code should be returned.
{
"type": "https://api.example.com/types/profile/errors/validations/invalid-user",
"title": "Invalid data provided",
"status": 400,
"detail": "One or more fields failed validation",
"instance": "123456789",
"invalidParams": [
{
"field": "customerId",
"message": "Required field is missing",
"code": "https://api.example.com/types/profile/errors/validations/too-long"
}
]
}
Validation Error Response - Multiple Fields
The following sample shows a validation error of the same type in two fields.
Note that invalidParams
is an array listing all the instances in the error.
Because both these are a client errors, a 400 Bad Request
HTTP status code should be returned.
{
"type": "https://api.example.com/types/profile/errors/validations/invalid-user",
"title": "Invalid data provided",
"status": 400,
"detail": "One or more fields failed validation",
"instance": "123456789",
"invalidParams": [
{
"field": "customerId",
"message": "Required field is missing",
"code": "https://api.example.com/types/profile/errors/validations/too-long"
},
{
"field":"name",
"message":"Name must be < 20 chars",
"code": "https://api.example.com/types/profile/errors/validations/too-long"
}
]
}
Validation Error Response - Bulk
For heterogeneous types of client-side errors shown below resulting from a bulk request, the error payload includes an array named errors
.
Each error instance is represented as an item in this array.
Because these are client validation errors, a 400 Bad Request
HTTP status code should be returned.
{
"type": "https://api.example.com/types/profile/errors/bulk/failed",
"title": "Invalid data provided",
"status": 400,
"detail": "One or more of the bulk requests failed.",
"instance": "123456789",
"errors": [
{
"type": "error://api.example.com/types/profile/errors/validations/invalid-user",
"title": "Invalid data provided",
"detail": "One or more fields failed validation",
"status": 400,
"instance": "123456789",
"invalidParams": [
{
"field": "customerId",
"message": "Required field is missing",
"code": "error://api.example.com/types/profile/errors/validations/missing"
},
{
"field": "name",
"message": "Name must be < 20 chars",
"code": "error://api.example.com/types/profile/errors/validations/too-long"
}
]
},
{
"type": "error://api.example.com/types/profile/errors/bulk/success",
"title": "Individual request within bulk request succeeded",
"detail": "Request succeeded",
"status": 200,
"instance": "123456789"
},
{ ... }
]
}
Semantic Validation Error Response
In cases where client input is well-formed and valid but the request action may require interaction with APIs or processes outside of this URI, an HTTP status code 422 Unprocessable Entity
should be returned.
{
"type": "error://api.example.com/types/profile/errors/account/balance-error",
"title": "Insufficient balance.",
"status": 409,
"detail": "The account balance is too low. Add balance to your account to proceed.",
"instance": "123456789"
}
Error Declaration In API Specification
It is important that documentation generation tools and client/server-side binding generation tools recognize RFC 7807. Following section shows how you could refer to the Zalando definition in an API specification confirming to OpenAPI.
{
"responses": {
"200": {
"title": "Address successfully found and returned.",
"schema": {
"$ref": "address.json"
}
},
"403": {
"title": "Unauthorized request. This error will occur if the SecurityContext header is not provided or does not include a party_id.",
"schema": {
"$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem"
}
},
"404": {
"title": "Address does not exist.",
"schema": {
"$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem"
}
},
"default": {
"title": "Unexpected error response.",
"schema": {
"$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem"
}
}
}
}
Documentation Standards
Documentation of error conditions is just as important as the documentation of intended use. Since the range of failure modes is typically larger than success, efforts to provide comprehensive error documentation will
Samples with Error Scenarios in Documentation
The User Guide of an API is a document that is exposed to API consumers. In addition to linking to samples showing successful execution for invocation on various methods exposed by the API, the API developer should also provide links to samples showing error scenarios. It is equally, or perhaps more, important to show the API consumers how an API would propagate errors in a machine-readable form in order to build applications that take necessary actions to handle errors gracefully and in a meaningful manner. In conclusion, we reiterate the message we started with that non-human consumers of RESTful APIs need more help to take necessary actions to resolve an error in a machine-readable manner. Therefore, a representation of errors following the schema described here MUST be returned by APIs for any HTTP status code that falls into the ranges of 4xx and 5xx.