Errors
The Archive API reports failures at three levels. Which one you are looking at is determined entirely by the HTTP status and the shape of the body, so check those first.
The decision tree
1. HTTP status is not 200 → transport error. Authentication, a missing header, or an unknown
workspace fails before any of your query’s fields are resolved. The body is a single string under
errors (429 is the exception — see the table below):
{ "errors": "<message>" }
{
"errors": "WORKSPACE-ID header is required"
} 2. HTTP status is 200 but the body has an errors array → GraphQL error. The request was
transported and parsed, but a field failed. Each entry carries a message, a machine-readable
extensions.code, and locations / path pointing into your query:
{
"errors": [
{
"message": "Preset \"00000000-0000-0000-0000-000000000000\" was not found in the current workspace.",
"extensions": {
"code": "WRONG_VIEW_TYPE_FOR_ITEMS",
"allowedAccessors": [
"media_deck",
"collections"
]
},
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"items"
]
}
],
"data": null
} Branch on extensions.code — here WRONG_VIEW_TYPE_FOR_ITEMS — not on the message text.
A response can carry both data and errors: when a dependency is temporarily unavailable, the
parts that could be resolved are still returned alongside a coded error for the parts that could not.
Read errors even when data is populated.
3. HTTP 200 with data and no errors → success, but a mutation may still report a business
failure. Every mutation payload has a userErrors array; an empty array means the write succeeded.
{
"data": {
"createCollection": {
"collection": {
"id": "17ccdf4e-19d2-5b30-9787-d4f0886ddf29",
"name": "Holiday Favorites",
"itemCount": 0
},
"userErrors": []
}
}
} The userErrors pattern
Mutations do not raise validation failures as GraphQL errors — they return them in userErrors, so
the request still comes back HTTP 200 with data. Each UserError has the shape:
type UserError {
field: [String!]
message: String!
}
Always inspect userErrors on a mutation response before treating it as a success. field points at
the offending input path; message is human-readable.
HTTP status reference
| Status | Body shape | Cause |
|---|---|---|
200 | data (+ optional errors[]) | Success, or a GraphQL field error / mutation userErrors |
400 | {"errors":"WORKSPACE-ID header is required"} | Missing WORKSPACE-ID on a workspace-scoped operation |
401 | {"errors":"Missing or invalid authentication token"} | Missing, invalid, or disabled token |
404 | {"errors":"Shop not found"} | Unknown workspace, or one your token can’t reach |
429 | {"errors":[{"message":"Rate limit exceeded…","extensions":{"code":"RATE_LIMIT_EXCEEDED"}}]} | Request-frequency wall or weighted-cost budget exhausted — honor Retry-After |
5xx | typically {"errors":"<message>"} | Infrastructure failure. Most server-side errors arrive as HTTP 200 instead — see the codes below |
Server-side errors arrive as HTTP 200
A failure inside the server does not usually change the status code — it comes back in the GraphQL
error envelope, so branch on extensions.code rather than waiting for a 5xx:
extensions.code | Meaning |
|---|---|
QUERY_TIMEOUT | The query was canceled by the database statement timeout. Narrow the time range or the page size, then retry |
INTERNAL_SERVER_ERROR | Unexpected server-side failure. Retry with backoff; contact support if it persists |
SERVICE_UNAVAILABLE | A dependency is temporarily unavailable. The data that could be resolved is still returned — retry later for the rest |
For the unknown-workspace case:
{
"errors": "Shop not found"
} See the Rate limits guide for 429 handling and the Authentication guide for 401 handling.