One signed binary. Every feature compiled in. Free to run. Install Crowkis →
← back to the Roost
curva guidesOctober 3, 2026· 6 min read

Curva troubleshooting: every error code and its fix

Every Curva error in one place: status, type, the SDK exception it raises, the usual causes and the fix, plus surprises that are not errors at all.

Curva errors all share one shape: an HTTP status and a JSON body `{"error": {"type": "...", "message": "..."}}`, where `type` is a fixed word such as `invalid_request` and `message` names the problem. The Python and TypeScript SDKs turn each status into a `CurvaError` subclass, so you can catch one case at a time. This page lists every status with its type, the exception it raises, the usual causes and the fix. It ends with things that surprise people but are not errors at all: verbal mode, `calibrated: false`, abstains and cache misses.

The error envelope: {error: {type, message}} and x-request-id

Every error from the HTTP API looks the same:

json
{"error": {"type": "invalid_request", "message": "..."}}

Branch on `type`, not on the message text. The message is for people: a 422 names the question or image that is wrong.

Every response, error or not, also carries an `x-request-id` header. The same id appears in the server's log line for the request. When a provider fails, its own words stay in the server log and are never sent to callers, so the request id is how you find them. You can send your own id (1 to 64 visible ASCII characters) to follow a request across services. The Python SDK exposes it as `d.request_id`, and the TypeScript SDK as `requestId`.

Status to exception: AuthError, InvalidRequestError, NotFoundError, RateLimitError, ModelError

Both SDKs raise a `CurvaError` with `status`, `type` and `message` for every failure. Catch a subclass when you care about one case:

The clients retry 429, 500, 502, 503 and 504 three times by default and honour `Retry-After`. TypeScript caps that wait at 30 s. You only see a `RateLimitError` or `ModelError` after the retries ran out.

python
from curva import CurvaError, RateLimitError, InvalidRequestError

try:
    d = client.decide(state, QUESTIONS)
except RateLimitError as e:
    wait(e.retry_after)
except InvalidRequestError as e:
    log.error("bad question: %s", e.message)    # a 422 names the question
except CurvaError as e:
    log.error("%s %s", e.status, e.type)

The diagram below is the short version of this page: find your status, follow it to the fix.

Curva errors by status, from symptom to fix
  1. 1
    CurvaError
  2. 2
    status 0
  3. 3
    start the server, check CURVA_BASE_URL
  4. 4
    400, 413, 422
  5. 5
    fix the request; read the message
  6. 6
    401, 403, 404
  7. 7
    check the key, its project, the id
  8. 8
    429, 502, 504
  9. 9
    wait for Retry-After, check the model id

Request errors mean change the request; capacity and provider errors mean wait or check the model.

Status 0: the server never answered

Status 0 is not an HTTP status. It means the client could not reach a server at all. Usual causes:

In Python, `curva.local()` and the module-level `curva.decide` start a private server for you. With no provider key at all, `curva.decide` raises an error naming the variables to set, such as `OPENROUTER_API_KEY`.

Request errors: 400, 413, 422

These mean the request itself is wrong. Retrying without a change gives the same error.

**400 `invalid_json`.** The body is not JSON, or it has an unknown `mode` or question `type`. In no-code tools the usual cause is mapped text with quotes or newlines pasted into a JSON template. Build the body with a JSON helper or a code step.

**413 `too_large`.** The body is over the server's limit, 16 MB by default. Large images are the usual cause. Raise the limit with `curva serve --max-body-mb`, or send smaller images.

**422 `invalid_request`.** The request is JSON, but something in it is not allowed. The message names the question or image. Causes from the reference:

For feedback, 422 means the label isn't a valid answer: use the option key for a Choice, the level index for a Score, and `true` or `false` for a Noul.

Access errors: 401, 403, 404

**401 `unauthorized`.** The server has API keys and none was sent, or the key is unknown or revoked. Revoking takes effect immediately. One trap: a server started without keys stays open until it is restarted, so restart after creating the first key, and expect callers without a key to start failing then.

**403.** The key was made with `--project` and the request is for another project. Use a key for that project, or one without a project binding.

**404 `not_found`.** An unknown decision, question or route. Three causes catch people out:

Capacity and provider errors: 429, 502, 504

**429 `rate_limited`.** One of three limits was hit: the API key's requests per minute (default 600), the model provider's rate limit, or the server's daily budget from `CURVA_DAILY_LIMIT`. Every 429 carries `Retry-After` in seconds. For the daily budget, that is the time until 00:00 UTC. The SDKs wait and retry for you.

**502 `model_error`.** Every model in the chain failed after retries. Details stay in the server log, so look up the `x-request-id` there. A fallback chain (`model` as a list) moves to the next model on 429 or a server error, which is the fix for a flaky provider.

**502 `model_unavailable`.** The model doesn't exist, was retired, or isn't open to your provider account. Providers rename models often. Check the id and your access. Retrying won't help.

**504 `timeout`.** The request took longer than the 120 s deadline. Each provider call also times out after 60 s by default. For a provider that queues, raise `CURVA_PROVIDER_<NAME>_TIMEOUT` (1 to 600 s).

Not errors, but surprising: verbal mode, calibrated false, abstains, cache misses

Some behaviour looks like a bug and is working as designed.

Next steps

The error table is in the [HTTP API reference](https://itsmohitrohilla.github.io/curva-docs/reference/http-api/), and the SDK classes are in the [Python SDK reference](https://itsmohitrohilla.github.io/curva-docs/reference/python-sdk/). For retry patterns in code, read [Python LLM API error handling](/blog/python-llm-api-error-handling/). For the raw HTTP contract, see [LLM decisions over HTTP](/blog/llm-decisions-over-http/), and for what Curva is, [what is Curva](/blog/what-is-curva/). Install with `pip install curva-ai`.