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

# Errors

> The error object, error types, and the codes you can branch on.

The API uses HTTP status codes to say whether a request worked. `2xx` means success, `4xx` means something about the request needs to change, and `5xx` means something went wrong on our side.

Every error response has the same body:

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "registration_number: String must contain at least 1 character(s)",
    "param": "registration_number",
    "request_id": "req_h7k2m9q4w1x8c3v6b5n0z2r4"
  }
}
```

| Field | Description |
| - | - |
| `type` | The broad category, decided by the HTTP status. See below. |
| `code` | A stable, machine-readable string. **Branch on this.** |
| `message` | A human-readable explanation. It can change wording at any time, so don't parse it. For `5xx` errors it is a generic message. |
| `param` | The request field the error is about, when there is one. For validation errors with several problems, it names the first and `message` lists them all. |
| `request_id` | The same value as the `X-Request-Id` response header. Include it when you contact support. |

## Error types

| `type` | HTTP status |
| - | - |
| `authentication_error` | 401 |
| `permission_error` | 403 |
| `invalid_request_error` | Other `4xx`: 400, 404, 409, 422 |
| `rate_limit_error` | 429 |
| `api_error` | `5xx` |

## Error codes

These are the codes you are most likely to see. New codes can be added; treat an unknown code according to its HTTP status.

| Status | `code` | Meaning and what to do |
| - | - | - |
| 401 | `missing_api_key` | No `Authorization: Bearer <key>` header. Send your key. |
| 401 | `invalid_api_key` | The key is malformed, unknown, revoked or expired, it's a live key where live mode isn't available, or its fleet is inactive. See [Authentication](/authentication#authentication-failures). |
| 401 | `wrong_environment` | A live key on the stage URL, or a sandbox key on the production URL. Use the URL for the key's environment. See [Authentication](/authentication). |
| 403 | `missing_scope` | The key lacks the scope this endpoint needs; the message names it. Create a key with that scope. |
| 403 | `sandbox_only` | You called a `/sandbox/*` endpoint with a live key. These endpoints only work with a sandbox (`cxk_test_`) key. See [Sandbox](/guides/sandbox). |
| 400 | `parameter_invalid` | A body, query or path value failed validation, or `Idempotency-Key` is longer than 255 characters. `param` names the field. |
| 404 | `resource_missing` | No such driver, vehicle, session, funding, invoice, endpoint or event in **your** fleet. |
| 409 | `conflict` | The request conflicts with the current state. Examples: the fleet already has 10 webhook endpoints, or you tried to redeliver to a disabled endpoint. Read `message`. |
| 400 | `idempotency_key_required` | `POST /wallet/fundings` and `POST /invoices/usage/pay` need an `Idempotency-Key` header. See [Idempotency](/idempotency). |
| 422 | `idempotency_key_reused` | You reused an `Idempotency-Key` with a different method, path or body. Use a new key for a new request. |
| 409 | `idempotency_key_in_progress` | A request with this `Idempotency-Key` is still running. Wait and retry with the same key. |
| 429 | `rate_limited` | Too many requests. Wait for `Retry-After` seconds. See [Rate limits](/rate-limits). |
| 409 | `funding_not_pending` | `POST /sandbox/fundings/{fundingId}/succeed` or `/fail`: the top-up has already been settled. The message gives its status. |
| 400 | `invoice_not_payable` | `POST /invoices/usage/pay`: at least one id isn't an unpaid usage invoice of your fleet. `param` is `invoice_ids`. |
| 404 | `route_not_found` | There's no endpoint at this method and path. Check the URL and the `/api/public/v1` prefix. |
| 500 | `internal_error` | Something unexpected went wrong. Retry with backoff; for `POST`s reuse the same `Idempotency-Key`. |

You may also see these codes, which come from the HTTP status when no more specific code applies:

| Status | `code` | Typical cause |
| - | - | - |
| 400 | `bad_request` | A request that is well-formed but not acceptable: an invalid or mismatched pagination `cursor`, a webhook URL that isn't public `https`, a missing photo file. |
| 400 | `amount_too_large` | `POST /invoices/usage/pay`: `amount` is more than the selected invoices' outstanding total. |
| 409 | `resource_already_exists` | A unique value is already taken, such as a vehicle registration number. |
| 502 | `pdf_unavailable` | The invoice PDF couldn't be generated. Retry later. |
| 503 | `service_unavailable` | A dependency is unavailable, for example the payment provider couldn't start a checkout. Retry later. |

### Portal errors

The fleet portal's **Developers** pages can show this code. The API itself never returns it.

| Status | `code` | Meaning |
| - | - | - |
| 409 | `live_keys_disabled` | You tried to create a live key, or open the live view of **Developers**, in a portal where live mode isn't available. Live keys come from the production portal; use a sandbox key here. |

## Handling errors

```javascript theme={null}
const res = await fetch(url, options);
if (!res.ok) {
  const { error } = await res.json();
  switch (error.code) {
    case 'rate_limited':
      await sleep(Number(res.headers.get('Retry-After')) * 1000);
      return retry();
    case 'idempotency_key_in_progress':
      await sleep(1000);
      return retry();
    default:
      log.warn({ requestId: error.request_id, code: error.code }, error.message);
      throw new Error(error.message);
  }
}
```

Retry `429` and `5xx` responses with exponential backoff. Don't retry other `4xx` responses unchanged: they will fail the same way.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.