# Errors

The Archive API reports failures at three levels. You can tell which level failed from the HTTP status and the shape of the response body, so check those first.

## Identifying the error level

**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):

```json
{ "errors": "<message>" }
```

```json
{
  "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:

```json
{
  "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
}
```

Match on `extensions.code` (`WRONG_VIEW_TYPE_FOR_ITEMS` in this example) rather than on the message text, which can change.

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.** A mutation may still report a business failure, though. Every mutation payload has a `userErrors` array; an empty array means the write succeeded.

```json
{
  "data": {
    "createCollection": {
      "collection": {
        "id": "17ccdf4e-19d2-5b30-9787-d4f0886ddf29",
        "name": "Holiday Favorites",
        "itemCount": 0
      },
      "userErrors": []
    }
  }
}
```

## The userErrors pattern

Mutations don’t 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:

```graphql
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.

> **Check userErrors, not the status code**
>
> A mutation can write nothing and still return HTTP 200. The signal is a non-empty `userErrors` array, never the status code.

## 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 access                                               |
| `429`  | `{"errors":[{"message":"Rate limit exceeded…","extensions":{"code":"RATE_LIMIT_EXCEEDED"}}]}` | Request rate limit or weighted cost budget exceeded. 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 doesn’t usually change the status code. It comes back in the GraphQL error envelope, so match 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:

```json
{
  "errors": "Shop not found"
}
```

See the [Rate limits guide](/api/v2/docs/guides/rate-limits) for 429 handling and the [Authentication guide](/api/v2/docs/guides/authentication) for 401 handling.
