Handling errors
In this article:
Introduction
When an error occurs, the API responds with detailed information about what went wrong and what part of the request caused it. An error response can fall into one of two categories: Internal errors and validation errors.
Regardless of the type of error that occurred, the response will always be structured in the same way, containing a message
describing the problem, a category
listing what type of error occurred, and a locations
entry providing the position in the offending request which caused the error. Finally, a path
entry is provided, which gives the path to the query or mutation in the request, which caused the error; this can be especially useful if multiple queries or mutations are batched together in one request.
Also read: API Rate Limits
Errors during mutations
If any error occurs during a mutation, all changes performed by the request will be canceled. Therefore, if - for any reason - a mutation fails, your data will remain the same as if the mutation had not been executed at all. This ensures the integrity of your data and makes it clear to understand the state of the system at any time.
Internal errors
When an error occurs on our end, you will be presented with an error similar to the following:
{
"errors": [
{
"message": "Internal server error",
"category": "internal",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"orders"
]
}
],
"data": []
}
Validation errors
Whenever a query or mutation is performed, which takes input, the provided input is validated to ensure that the resulting data is valid, ideally making it impossible to put your solution into an invalid or corrupted state. If input is deemed invalid, then the content
field of the resulting payload will be null
, and details on the validation errors are provided in the errors
field.
All errors in any errors
field of a payload implement the ClientErrorInterface
interface:
interface ClientErrorInterface {
field: [String!]!
message: String!
name: String!
}
Types implementing this interface are grouped by business domain, so that related queries and mutations use the same concrete error type. This is demonstrated in the following example, with PageTreeClientError
as the concrete type.
Each of these fields are explained in more detail in the following examples.
Example: Creating a page
In this example, we attempt to create a new page.
Our GraphQL request is as follows:
QUERY
mutation pageCreate($input: PageCreateInput!) {
pageCreate(input: $input) {
content {
id
}
errors {
field
message
name
code
id
}
}
}
VARIABLES
{
"input": {
"folderId": 1,
"parentId": 1,
"languageLayerAccess": [
1,
2
],
"menuDisplaySettings": [
1
],
"translations": [
{
"languageId": 1,
"data": {
"link": "pageLink1"
}
},
{
"languageId": 2,
"data": {
"title": "pageTitle2"
}
}
],
"type": "TEXT",
"userAccessSettings": [
1
]
}
}
Response
{
"data": {
"content": null,
"errors": [
{
"field": [
"input",
"folderId"
],
"message": "Unexpected 'folderId'. Expected exactly one of: ['folderId', 'parentId'].",
"name": "EXCLUSIVE_OR_UNEXPECTED_ERROR",
"code": "EXCLUSIVE_OR_UNEXPECTED_ERROR",
"id": "EXCLUSIVE_OR_UNEXPECTED_ERROR"
},
{
"field": [
"input",
"parentId"
],
"message": "Unexpected 'parentId'. Expected exactly one of: ['folderId', 'parentId'].",
"name": "EXCLUSIVE_OR_UNEXPECTED_ERROR",
"code": "EXCLUSIVE_OR_UNEXPECTED_ERROR",
"id": "EXCLUSIVE_OR_UNEXPECTED_ERROR"
},
{
"field": [
"input",
"parentId"
],
"message": "Page with id '123' does not exist.",
"name": "ENTITY_NOT_FOUND_ERROR",
"code": "ENTITY_NOT_FOUND_ERROR",
"id": "ENTITY_NOT_FOUND_ERROR"
},
{
"field": [
"input",
"translations"
],
"message": "Missing required 'title' in primary language: 1",
"name": "MISSING_PRIMARY_TITLE_ERROR",
"code": "MISSING_PRIMARY_TITLE_ERROR",
"id": "MISSING_PRIMARY_TITLE_ERROR"
}
]
}
}
This then means that our input violates 4 different (but not unique) constraints:
input.folderId
: Exactly one offolderId
andparentId
fields are expected.input.parentId
: Exactly one offolderId
andparentId
fields are expected.input.parentId
: The given ID of123
doesn't refer to a page that exists.input.translations
: We didn't provide atitle
translation in the primary language, this case language with id1
.
There are a few things to note here:
While 4 constraints on the input were violated, both (2) and (3) were associated with the same field in the input, and two of the violations were caused by the same constraint - but on two differed fields. Finally, while the absence of a `title` translation in a primary language *could* have had its field listed as `input.translations.0`, this was not the case; this is so that even if no translations for the primary language are provided, the context of the violation is still clear.
What this also demonstrates is that the PageTreeClientError
type includes two additional fields not present in ClientErrorInterface
: name
and id
.
The full definition of the PageTreeClientError
is:
interface ClientErrorInterface {
field: [String!]!
message: String!
name: String!
}
The id
field contains a unique machine-readable identifier for the error type. Currently, this will have the same value as the name
field in many cases, however this will be a UUID in the future.
The code
field contains the error-code for errors of this type, and is an enumeration, which allows developers to inspect the schema and know exactly which errors can be expected from a given query or mutation.
While it may seem superfluous to have both the name
and code
fields, the reasoning behind this is that having a string version of the code
field allows API clients to write general fragment selects, such as:
fragment ClientErrorFields on ClientErrorInterface {
field
message
name
}
This can then be leveraged to simplify the above pageCreate
mutation:
mutation pageCreate($input: PageCreateInput!) {
pageCreate(input: $input) {
content {
id
}
errors {
...ClientErrorFields
code
id
}
}
}
fragment ClientErrorFields on ClientErrorInterface {
field
message
name
}
Validation errors (deprecated)
Another type of error that can occur is a validation error. This occurs if some of the provided data is invalid. The following example demonstrates this by attempting to create a page inside a folder that does not exist:
POST shop99999.mywebshop.io/api/graphql
(Replace "shop99999" with your own shop number)
mutation {
pageCreate(input: {folderId: 123, type: text}) {
id
}
}
Response
{
"errors": [
{
"message": "The given data was invalid.",
"category": "graphql",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"pageCreate"
],
"validation": {
"input.folderId": [
"validation.exists"
]
}
}
],
"data": []
}
In the case of validation errors, the errors
entry of the response also contains a validation
section, which will enumerate all validation errors, indexed by the path to the invalid data in the input.