Errors¶
Guidance
- Error fields should follow RFC 7807
- An operation should be able to return multiple errors in one response.
- The
status
field should match the HTTP status code being returned. - The
title
field should be the generic class of the error and consistent across the API. - The
detail
field should be specific to the error at hand. - If an error is in the request, a
source
field should point to it.
Required Errors¶
Returning reliable, consistent, and descriptive errors from responses is crucial for maintaining an API's usability as it facilitates quicker diagnosis and mitigation of issues by both consumers and API teams. VA has two sets of error status codes for standardized error reporting. One of the sets contains standard error status codes, and API teams must include them in all endpoints. The other set contains error status codes related to errors that occur upstream. If the API depends on upstream services, teams must use both sets of error codes.
The table below contains the required error status codes that all APIs must use.
Status Code | Description |
---|---|
400 | Bad Request |
401 | Unauthorized |
403 | Forbidden |
404 | Not Found |
429 | Too Many Requests |
500 | Internal Server Error |
If your API has endpoints dependent on upstream services, you must also include the following error status codes.
Status Code | Description |
---|---|
502 | Bad Gateway |
503 | Service Unavailable |
504 | Gateway Timeout |
For 5xx
level errors returned from upstream services, error mapping must be implemented
so that your API returns the desired error responses to the consumer and does not
just pass along the error responses from the upstream services. This must be done
to ensure that the consumer does not receive system details or other sensitive information
about the upstream services.
The section, Choosing an error code describes these error codes in more detail and can provide assistance with selecting the correct error codes.
Error Schemas¶
The error schema should match that of the information model you are using. This guide recommends using JSON::API unless an industry-specific format like FHIR is required. We recommend JSON::API because it has a well thought-out, extensible, and relatively simple error model.
An example of a JSON::API-formatted 422 'Unprocessable Entity' error is:
{
"errors": [
{
"status": "422",
"source": { "pointer": "/data/attributes/isRefillable" },
"title": "Invalid Attribute",
"detail": "isRefillable must be a boolean value."
}
]
}
FHIR has a different error schema (OperationOutcome resource). An example is:
{
"resourceType": "OperationOutcome",
"id": "searchfail",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n<p>The "name" parameter has the modifier "exact" which is not supported by this server</p>\n</div>"
},
"issue": [
{
"severity": "fatal",
"code": "code-invalid",
"details": {
"text": "The \"name\" parameter has the modifier \"exact\" which is not supported by this server"
},
"location": [
"http.name:exact"
]
}
]
}
Multiple errors¶
Operations should be able to return multiple errors and providers should return
errors for all the issues with the request at once. For example, if 2 fields are
invalid, return both so the consumer is aware of all the issues that must be corrected,
instead of raising an error once the first invalid field is processed. If the error
statuses are different but have the same hundredth, for example, both are 4xx
,
return the base generic value (400
). If the errors have mixed hundredth values
return a 500
.
{
"errors": [
{
"status": "422",
"source": { "pointer": "/data/attributes/isRefillable" },
"title": "Invalid Attribute",
"detail": "isRefillable must be a boolean value."
},
{
"status": "422",
"source": { "pointer": "/data/attributes/contactEmail" },
"title": "Invalid Attribute",
"detail": "contactEmail must be a valid email address."
}
]
}
Choosing an error code¶
Choosing the correct HTTP status code for an error can be confusing. With VA APIs, the error will originate from one of three sources: from the consumer’s request; an upstream service the API depends on; or the API itself, such as the web application and its components.
- Request errors: Issues caused by requests are a flavor of a
400
error (4xx
). - Upstream errors: Errors from upstream services are
502
,503
, or504
. - Application errors: Always return a
500
.
The flowchart below can help you decide which HTTP status code to return for an error.
Flowchart illustrating error handling for an API. Starting at the decision diamond asking 'Problem with the request?', if 'Yes,' it leads to a series of Gateway Managed Errors: 'Is the user authenticated?' If 'No', it results in a 401 error. If ‘Yes’, it asks 'Is the user authorized?' If 'No' it results in a 403 error. If ‘Yes’, it asks 'Are they under the rate limit?' if 'No', it results in a 429 error. If 'Yes,' it asks 'Does the resource exist?' If ‘No’, it results in a 404 error. If ‘Yes’, it asks 'Is the body's syntax correct?' If 'No', it results in a 400 error. If ‘Yes’, it asks 'Are the body's values valid?' if 'No', it results in a 422 error. If 'Yes', it results in a 400 error. If there is not a problem with the request, this leads to a decision diamond asking 'Problem in an upstream service?' If 'Yes', it proceeds to 'Is the upstream service up?' If ‘No’, it results in a 503 error. If ‘Yes’, it asks ‘Is the upstream service responding in time?’ If ‘No’, it results in a 504 error, if ‘Yes’, it results in a 502 error. Lastly, if there is not a problem in an upstream service, this leads to a decision diamond asking 'Problem in our service?' ‘Yes’, proceeds to 'Is our service up? If ‘No’, it results in a 503 error. If ‘Yes’, it asks ‘Is our service responding in time?’ If ‘No’, it results in a 504 error. If 'Yes', it results in a 500 error.