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

# Errors

> Error response format and error codes

All errors follow a consistent JSON format with a machine-readable `code` and human-readable `message`.

## Error response format

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": {
    "code": "validation_error",
    "message": "keyword: Required",
    "details": { ... },
    "request_id": "req_abc123"
  }
}
```

| Field        | Description                                      |
| ------------ | ------------------------------------------------ |
| `code`       | Machine-readable error code (see table below)    |
| `message`    | Human-readable explanation                       |
| `details`    | Additional context (optional)                    |
| `request_id` | Unique request identifier for support (optional) |

<Note>
  Always include the `request_id` when contacting support about a failed request. It allows the team to trace the exact request through the system.
</Note>

## Error Handling Decision Tree

Use this diagram to determine the correct recovery strategy for each error class:

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart TD
    Error["API returns error"] --> StatusCheck{"HTTP status?"}
    StatusCheck -->|"400"| Fix400["Fix request: check params, body, format"]
    StatusCheck -->|"401"| Fix401["Check API key is valid and included"]
    StatusCheck -->|"403"| Fix403["Check scope permissions for this endpoint"]
    StatusCheck -->|"404"| Fix404["Check resource ID or path"]
    StatusCheck -->|"409"| Fix409["Idempotency conflict: same key, different params"]
    StatusCheck -->|"429"| Retry429["Back off and retry after rate limit window"]
    StatusCheck -->|"500+"| Retry5xx["Retry with exponential backoff"]
    Retry429 --> Success["Success"]
    Retry5xx --> Success
```

<Warning>
  **Do not retry 4xx errors** (except `429`). These indicate a problem with the request itself — retrying the same request will always fail. Fix the issue first.
</Warning>

## HTTP status codes

| Status | Meaning                               |
| ------ | ------------------------------------- |
| `200`  | Request succeeded                     |
| `201`  | Resource created                      |
| `202`  | Async operation accepted and started  |
| `204`  | Deleted (no response body)            |
| `400`  | Invalid request parameters            |
| `401`  | Authentication failed                 |
| `403`  | Authenticated but lacking permissions |
| `404`  | Resource not found or not accessible  |
| `409`  | Idempotency conflict                  |
| `429`  | Rate limit exceeded                   |
| `500`  | Server error                          |

## Error codes

| Code                   | Status | Description                                              |
| ---------------------- | ------ | -------------------------------------------------------- |
| `validation_error`     | 400    | Request body or parameters failed validation             |
| `unauthorized`         | 401    | Missing or invalid API key                               |
| `key_expired`          | 401    | API key has expired                                      |
| `key_revoked`          | 401    | API key has been revoked                                 |
| `forbidden`            | 403    | Key lacks the required scope                             |
| `not_found`            | 404    | Resource does not exist or is not accessible to this key |
| `idempotency_conflict` | 409    | Same idempotency key used with different parameters      |
| `rate_limit_exceeded`  | 429    | Too many requests                                        |
| `internal_error`       | 500    | Unexpected server error                                  |

<Tip>
  For `429` errors, see the [Rate Limiting](/rate-limiting) guide for retry logic examples with exponential backoff.
</Tip>

## Handling errors

<CodeGroup>
  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const response = await fetch(url, { headers });

  if (!response.ok) {
    const { error } = await response.json();

    switch (error.code) {
      case "validation_error":
        console.error("Bad request:", error.message);
        break;
      case "rate_limit_exceeded": {
        const retryAfter = response.headers.get("Retry-After");
        await new Promise((r) => setTimeout(r, Number(retryAfter) * 1000));
        break;
      }
      case "unauthorized":
      case "key_expired":
      case "key_revoked":
        console.error("Auth issue:", error.message);
        break;
      default:
        console.error("API error:", error.code, error.message);
    }
  }
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import time
  import requests

  response = requests.get(url, headers=headers)

  if not response.ok:
      error = response.json()["error"]

      if error["code"] == "validation_error":
          print(f"Bad request: {error['message']}")
      elif error["code"] == "rate_limit_exceeded":
          retry_after = int(response.headers.get("Retry-After", 5))
          time.sleep(retry_after)
          # retry the request
      elif error["code"] in ("unauthorized", "key_expired", "key_revoked"):
          print(f"Auth issue: {error['message']}")
      else:
          print(f"API error: {error['code']} {error['message']}")
  ```

  ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}}
  $ch = curl_init($url);
  curl_setopt_array($ch, [
      CURLOPT_HTTPHEADER => ["X-API-Key: $apiKey"],
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HEADER => true,
  ]);
  $response = curl_exec($ch);
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  $body = json_decode(substr($response, $headerSize), true);

  if ($httpCode >= 400) {
      $error = $body["error"];

      switch ($error["code"]) {
          case "validation_error":
              echo "Bad request: " . $error["message"];
              break;
          case "rate_limit_exceeded":
              $retryAfter = (int) ($error["details"]["retry_after"] ?? 5);
              sleep($retryAfter);
              // retry the request
              break;
          case "unauthorized":
          case "key_expired":
          case "key_revoked":
              echo "Auth issue: " . $error["message"];
              break;
          default:
              echo "API error: " . $error["code"] . " " . $error["message"];
      }
  }
  ```
</CodeGroup>
