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

# Errors

> What comes back, what caused it, and what is worth retrying.

Every error body is JSON and carries a `request_id`. Include it when you contact
support — it is what lets us find your call in our logs.

```json theme={null}
{ "error": "invalid_key", "request_id": "ff14b2d0-60fc-46c2-bc62-45478100869a" }
```

| Status | `error`                    | Cause                                                                       |
| :----- | :------------------------- | :-------------------------------------------------------------------------- |
| 400    | `'text' is required`       | `text` missing or empty.                                                    |
| 400    | `invalid 'voice'`          | Not `Ananya` or `Arjun`. Case-sensitive; the body lists `available_voices`. |
| 400    | `invalid 'language'`       | Not a supported code. See [Languages](/reference/languages).                |
| 400    | `invalid 'model'`          | Unknown model; the body lists `available_models`.                           |
| 401    | `missing_or_malformed_key` | No `Authorization: Bearer …` header.                                        |
| 401    | `invalid_key`              | Key is wrong, revoked, or disabled.                                         |
| 502    | `origin_error`             | Transient server-side failure. Safe to retry.                               |
| 502    | `origin_unreachable`       | Could not reach the model. Retry with backoff.                              |

## Retry only what retrying fixes

**400 and 401 are deterministic** — the same request will fail identically
forever. Read the message instead of looping.

**502 and 5xx are worth retrying**, with backoff.

## A 403 that is not from us

<Warning>
  A `403` whose body is **not JSON** did not come from the API. It is a request
  filter reacting to your HTTP client's default User-Agent — not an
  authentication problem, though it reads like one and sends people hunting a
  bad key.

  Our errors are always JSON with a `request_id`. Setting any User-Agent clears
  it:

  ```python theme={null}
  headers["user-agent"] = "my-app/1.0"
  ```

  This affects a few libraries' defaults, notably Python's stdlib `urllib`.
  `requests`, `curl`, `node-fetch`, `axios`, `okhttp` and Go's `net/http` are
  unaffected.
</Warning>

## Check the status before you write the body

On any error the body is **JSON, not audio** — and `curl --output`, `wget -O` or
a naive `open(f,"wb").write(r.content)` will happily write that JSON into your
`.pcm` file. Converting it yields a silent 0-second clip, so a 400 shows up as
"the audio came back empty" and sends you debugging the wrong thing.

```bash theme={null}
curl --fail-with-body ...        # not just --output
```

## On the WebSocket

Errors arrive as frames and the socket **stays open** — correct the frame and
send it again:

```json theme={null}
{ "type": "error", "error": "invalid 'voice'", "available_voices": ["Ananya", "Arjun"] }
```
