# Errors

> How komplai's API reports an error, how to handle each status and when to try again, with every error code and what to do.

A failed call answers with an HTTP status and a JSON body:

```json
{
  "error": "invalid_body",
  "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
  "doc_url": "https://developers.komplai.dk/errors/invalid_body"
}
```

| Field | Contents |
| --- | --- |
| `error` | The code. Each code has its own page, listed [below](#codes). |
| `request_id` | The same id as the response's `Request-Id` header. Quote it when you ask komplai about a call. |
| `doc_url` | The page for the code, with what happened and what to do. |

The OpenAPI document describes this body as `components.schemas.Error`.

Some codes add fields that help you act: `hint` with [`engine_retired`](https://developers.komplai.dk/errors/engine_retired) and [`mapping_not_available`](https://developers.komplai.dk/errors/mapping_not_available), `state` with [`job_not_finished`](https://developers.komplai.dk/errors/job_not_finished), `nerDeployment` with [`ner_location_unavailable`](https://developers.komplai.dk/errors/ner_location_unavailable), `estimatedSeconds` and `holdSeconds` with [`use_job`](https://developers.komplai.dk/errors/use_job), and `pages` with [`scanned_pages_not_supported`](https://developers.komplai.dk/errors/scanned_pages_not_supported). The example on each code's page shows them. Apart from a Coding stream that has already begun, a failed call returns no partial result.

A few codes are the web server's own reason phrases, such as `Not Found` or `Payload Too Large`. Their bodies also carry the server's `message` and `statusCode`.

## Handle errors by status

Branch on the HTTP status first and the code second, and treat a code you do not know by its status.

| Status | What it means | What to do |
| --- | --- | --- |
| 400, 413, 415, 422 | The request, the text or the file cannot be processed as sent. | Correct it. The same request gets the same answer. |
| 401 | The key is missing, unknown, expired or revoked. | Check that you send the whole key, or create a new one. |
| 403 | The key may not do this: a scope is missing, the tool is not assigned to you, the processing is not approved, or an organisation integration key cannot use the operation. | Use a key with the right access, or ask your organisation's administrator. |
| 404 | No text job with that id for this key, a model or connection you named is not available to you, or no operation has that method and path. | Check the id, the key, the model or connection and the path. |
| 409 | The request conflicts with the current state. | Read the code: some ask you to send the request again, some to wait, and others need a change. |
| 410 | A text job ended without a result. | Submit the text again as a new job. |
| 429 | A limit is reached. | For a request limit, wait the seconds in `Retry-After`, then try again. For an AI spending limit, see below. |
| 500, 502, 503 | komplai or a service behind it failed or is unavailable. | Try again later. If the call may have reached a model, do not send it again automatically. |
| 504 | The time ran out. | After [`processing_timeout`](https://developers.komplai.dk/errors/processing_timeout), send the text as a text job. After [`timeout`](https://developers.komplai.dk/errors/timeout), the outcome is unknown. |

A response without a `Request-Id` header was answered before the API handled the call, for example by the gateway while the API restarts. Treat it by its status.

## When to try again

- **429 with `Retry-After`.** [`rate_limited`](https://developers.komplai.dk/errors/rate_limited) and [`processing_capacity_busy`](https://developers.komplai.dk/errors/processing_capacity_busy) say how many seconds to wait in `Retry-After`, at most 60. A 429 from the per-IP limit also has `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset`. [Limits](https://developers.komplai.dk/limits) lists the limits.
- **429 for AI spending.** After [`ai_credit_exhausted`](https://developers.komplai.dk/errors/ai_credit_exhausted) and [`person_ai_credit_exhausted`](https://developers.komplai.dk/errors/person_ai_credit_exhausted), waiting does not help: a monthly cap starts again next month. Choose local processing, or ask your organisation's administrator about the limit.
- **A model may have answered.** After a 502, 503 or 504 on a call that reaches a model, such as Chat or a synchronous Redact call with `llm`, the model may already have worked and EU cloud usage may already count towards the cap. Do not resend such calls automatically. Text jobs and Coding have their own protection against duplicates: see [Retries and idempotency](https://developers.komplai.dk/api#retries-and-idempotency).
- **Busy local infrastructure.** [`model_busy`](https://developers.komplai.dk/errors/model_busy) (503) on POST /v1/redact also sends `Retry-After`. A text job waits for its turn by itself.
- **Other 4xx answers** do not change by trying again. Correct the request, the key or the access first, unless the code's page tells you to send the request again, as [`processing_access_changed`](https://developers.komplai.dk/errors/processing_access_changed) does.

## Text jobs

A text job that fails ends in state `failed`, `cancelled` or `invalidated`, and GET /v1/redact/jobs/{id} names the reason in `error`. GET /v1/redact/jobs/{id}/result then answers 410. Codes that only end a text job, such as [`redact_job_stalled`](https://developers.komplai.dk/errors/redact_job_stalled), have no HTTP status of their own and are listed under **Text jobs** at the end of the index.

## Errors in the Coding stream

When POST /v1/coding/chat/completions fails after the stream has begun, the last event is `data: {"error": {"message": "…", "code": "…", "request_id": "…", "doc_url": "…"}}` without `data: [DONE]`. Treat the outcome as unknown.

## Codes

---

https://developers.komplai.dk/errors
