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

# Errors and rate limits

> Status codes, error shapes, and throttling.

Source: https://telluvian.ai/docs/errors

Errors are OpenAI-shaped: a JSON body with an `error` object carrying
`message`, `type`, and `code`. The one exception is `422`, covered below.

```json
{
  "error": {
    "message": "Insufficient credits. Top up your balance in the dashboard.",
    "type": "invalid_request_error",
    "code": "insufficient_credits"
  }
}
```

## Status codes

| Status | Meaning | What to do |
| --- | --- | --- |
| `400` | Malformed JSON, invalid Model Select controls, or analyze without probe opt-in. | Check the body; analyze requires `include_scores: true`. |
| `400` | `upstream_bad_request`: the model provider rejected the request, often a parameter it does not accept or a prompt longer than its context window. | Check `max_tokens`, `temperature` and the message format. |
| `400` | `byok_credential_rejected`: your own provider key was rejected and nothing else could serve the request. | Check the key under [Provider keys](https://telluvian.ai/dashboard/provider-keys). See [Bring your own key](https://telluvian.ai/docs/byok.md#errors). |
| `400` | `credential_not_found` / `credential_not_usable`: the request's `byok_credential` names no key, or a key that cannot serve this model or this API key. | Check the slug under [Provider keys](https://telluvian.ai/dashboard/provider-keys), or drop the field to use the default order. |
| `401` | Missing, unknown, or revoked API key. | Check the `Authorization` header, or mint a new key. |
| `402` | Balance exhausted, or (`byok_credential_rejected`) your provider account is out of credit. | [Add credit](https://telluvian.ai/dashboard/billing), or top up with the provider. |
| `403` | Account suspended, or the wrong kind of key: a management key on a model endpoint, or an ordinary key on `/v1/byok`. | [Contact us](mailto:hello@telluvian.ai), or use the [right key](https://telluvian.ai/docs/byok.md#managing-keys-with-the-api). |
| `404` | Unknown model, or unknown `previous_response_id`. | Call `GET /v1/models`, or check the id. |
| `413` | `upstream_request_too_large`: the request is too large for the model. | Shorten the prompt, or use a model with a larger context window. |
| `422` | Request body failed schema validation: a non-string `content`, or an [`-analyze`](https://telluvian.ai/docs/analyze.md) call with no `assistant` message. | Check the shape against [Compatibility](https://telluvian.ai/docs/compatibility.md). |
| `429` | Rate limited by us, or (`upstream_rate_limited`) the model is rate-limited by its provider. | Back off for `Retry-After` seconds, or [use your own provider key](https://telluvian.ai/docs/byok.md). |
| `502` | `upstream_unavailable`: the model provider returned an error. With your own key, `byok_credential_rejected`. | Retry with backoff, or try another model. |
| `503` | [Model Select](https://telluvian.ai/docs/model-select.md) is unavailable. | Retry with backoff, or name a model yourself. |
| `504` | `upstream_timeout`: the model provider did not respond in time. With your own key, `byok_credential_rejected`. | Retry with backoff. |

A provider failure is always answered in our words, with `type: "upstream_error"`
and one of the `upstream_*` codes above. The provider's own error text is never
passed on.

> **422 has a different body shape**
>
> A `422` comes from the model server's own validation and is relayed
> unchanged, so it carries `{"detail": ...}` rather than the `error` object
> above. It is a string for the analyze case, and a list of field errors for a
> schema violation. Nothing is billed for a rejected request.

A `404` for an unrecognised model carries `code: "model_not_found"` and names
the offending field in `param`:

```json
{
  "error": {
    "message": "The model 'gpt-9' does not exist or you do not have access to it. Call GET /v1/models for the available models, or see https://telluvian.ai/docs.",
    "type": "invalid_request_error",
    "param": "model",
    "code": "model_not_found"
  }
}
```

### Model Select

A [Model Select control](https://telluvian.ai/docs/model-select.md#the-controls) that cannot be
honoured is rejected before anything is generated, and nothing is billed. The
message names the field. A direct `/v1/modelSelect` call returns its own error
envelope with a stable `code`; see [Model Select errors](https://telluvian.ai/docs/model-select.md#errors). The common causes:

| Status | Cause |
| --- | --- |
| `400` | `xPerf` sent as a quoted number, or naming a model we do not know. |
| `400` | `xPerfEffort` sent alongside a numeric `xPerf`, or an effort the named model does not offer. |
| `400` | `xLatency` outside `instant`, `chat` and `batch`. |
| `400` | `modelZoo` matching no active Model Select candidate. |
| `400` | `sessionId` that is not a GUID. |
| `503` | Model Select is unavailable. Nothing is substituted. See [when Gallery is unavailable](https://telluvian.ai/docs/gallery.md#when-gallery-is-unavailable). |

A quality bar that nothing can meet is **not** an error: the request succeeds
on the best model available. Read `response.model` to see what ran.

## Rate limits

Chat Completions and Responses normally carry these headers when rate-limit
checks complete successfully:

- `X-RateLimit-Limit`, requests allowed in the current window
- `X-RateLimit-Remaining`, how many are left

A `429` additionally carries `Retry-After`, in seconds.

Limits are per API key, **600 requests per 60-second window by default**. That
is generous, because spend is already gated by your balance. It is an abuse
ceiling rather than a quota. A rejected request still counts against the
window, so hammering the endpoint keeps it pinned rather than earning a fresh
allowance. If you need a higher limit, [get in touch](mailto:hello@telluvian.ai).

> **The limiter fails open**
>
> Counting happens centrally, not per server instance. The gateway runs on
> several at once, and an in-process counter on each would both admit multiples
> of the intended limit and reset every time an instance scales. If that count
> itself is unreachable (a database blip), **the request is allowed through**
> rather than rejected: a limiter that hard-fails every request during a
> partial outage would turn it into a total one. Your balance is the backstop
> either way. A burst let through during a blip still has to be paid for.

> **Balances can dip slightly negative**
>
> The balance check happens before your request runs, but the cost is settled
> after it finishes. A request admitted with a small positive balance can take
> it just below zero, bounded by your `max_tokens`. The next request is then
> refused with a `402`.

## Streaming failures

A stream can fail after it has already started, after your client has
received a `200` and some content chunks. There is no structured SSE error
event for this: the connection is simply closed without a trailing
`data: [DONE]`.

> **A stream without [DONE] is a failure, not a slow finish**
>
> Treat any stream that ends without the literal `data: [DONE]` line as
> failed, not as "still coming." Retry the request rather than waiting.

Billing settles from whatever token counts were observed before the failure (or an estimate when final usage is unavailable),
so a request that fails partway through can still incur a (smaller) charge for
the tokens the model had already generated, the same way a partial response
you kept and used would be billed. This is the same settlement path either
way; see [Pricing](https://telluvian.ai/docs/pricing.md).
