> ## Documentation Index
> Fetch the complete documentation index at: https://docs.member.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Error handling

> How to read JSON:API errors and request ids.

When a request fails, Membership.io returns an `errors` array.

```json theme={null}
{
  "errors": [
    {
      "status": "404",
      "code": "hub_not_found",
      "title": "Not Found",
      "detail": "Hub not found.",
      "meta": {
        "request_id": "01J..."
      }
    }
  ]
}
```

## Request id

Every handled error includes a request id in `meta.request_id` and usually in the `X-Request-ID` response header. Include this id when reporting a bug or support issue.

## Validation errors

Validation errors use status `422`. When available, `source.pointer` points to the invalid request body field.

```json theme={null}
{
  "errors": [
    {
      "status": "422",
      "title": "Validation Error",
      "detail": "Field required",
      "source": {
        "pointer": "/data/attributes/email"
      }
    }
  ]
}
```

## Common statuses

| Status | Meaning                                                                                    |
| ------ | ------------------------------------------------------------------------------------------ |
| `400`  | The request is malformed or missing required context.                                      |
| `401`  | The token is missing, expired, invalid, or revoked.                                        |
| `403`  | The credential is valid but not allowed to access the resource.                            |
| `404`  | The resource does not exist or is hidden for authorization reasons.                        |
| `409`  | The request conflicts with current state, such as a duplicate or already-revoked resource. |
| `415`  | The content type is unsupported. Prefer `application/vnd.api+json`.                        |
| `422`  | The request body, path, or query parameters failed validation.                             |
| `429`  | The route-specific rate limit was exceeded. Check `Retry-After`.                           |

<Warning>
  **Do not branch on `errors[].status` from the `mio` CLI.** The CLI writes the same envelope shape to stderr, but through `v0.12.1` it reconstructs `status` from its own exit code, which is coarser than the API's status — so `403` is reported as `"401"`, `409` and `422` both as `"400"`, and `503` as `"500"`. Branch on `meta.exit_code` instead (`2` usage, `3` auth, `4` not found, `6` rate limited, `7` server), or call the API directly when you need to tell `403` from `401` or `409` from `422`. Reading the status verbatim is fixed on `main` and will land in the next release; the exit-code contract is unchanged either way.
</Warning>

## Rate limit errors

When a per-endpoint rate limit is exceeded the response carries code `rate_limited` and a `Retry-After` header with the number of seconds until the window resets.

```json theme={null}
{
  "errors": [
    {
      "status": "429",
      "code": "rate_limited",
      "title": "Too Many Requests",
      "detail": "Rate limit exceeded. Retry after 42 seconds.",
      "meta": { "request_id": "01J..." }
    }
  ]
}
```

See [Rate limits](/reference/rate-limits) for the full list of limited endpoints and their thresholds.
