---
sidebar_position: 8
title: Error codes
description: 4xx/5xx responses from the Lium API carry error.code (a stable machine string), a hint that says what to do next, and a request_id you can quote to support. This page lists the codes and the routes that still answer with the older flat body.
---

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

# Error codes

An error response from `https://lium.io/api` has this shape (the routes listed under [Routes with the older body](#routes-with-the-older-body) are the exception):

```json
{
  "success": false,
  "error": {
    "code": "node_unavailable",
    "message": "Node is not available.",
    "hint": "The node was rented or went offline between listing and renting; pick another (`lium ls`) or retry in a minute.",
    "request_id": "0fd17bdf43884d48af88bca99c884936"
  },
  "message": "Node is not available.",
  "status_code": 400,
  "error_id": null,
  "request_id": "0fd17bdf43884d48af88bca99c884936",
  "timestamp": "2026-09-07T01:48:42.756086",
  "api_context": null
}
```

- **`error.code`** is stable: key on it, not on the message text. The message may be reworded; the code will not.
- **`error.hint`** says what to do next in one sentence.
- **`error.request_id`** identifies this request in the platform's logs. The same value comes back in the `X-Request-Id` response header on every response, success or failure, and is the `error_id` of a 5xx. Quote it when you ask for help (`support@lium.io`, Discord `#support`): it points at every log line and the exception of that exact request.
- You can send your own `X-Request-Id` (8–64 characters of `A-Z a-z 0-9 . _ : -`) and the API will keep it, so an agent's run id can be found in the platform logs.
- The flat fields (`message`, `status_code`, `error_id`, `validation_errors`, `details`) are the older shape and stay for compatibility.

## The codes

| `error.code` | HTTP | When | What to do |
|---|---|---|---|
| `unauthorized` | 401 | No key or session, or the key is unknown | Send a valid key in `X-API-Key` ([API keys](/pod-users/api-keys)); `lium config get api.api_key` shows which key the CLI uses, masked |
| `api_key_inactive` | 401 | The key was deactivated or has expired | Create a new key |
| `insufficient_balance` | 403 | Balance is zero or negative (renting needs a positive balance covering 15 minutes of the node) | Top up at [lium.io/billing](https://lium.io/billing) |
| `account_balance_invalid` | 403 | The account's balance could not be read | Contact support with the `request_id`; topping up does not clear it |
| `email_not_verified` | 403 | The account's e-mail is not verified | Verify it, then retry |
| `forbidden` | 403 | The resource belongs to another account, or this key may not do that | Check which key the CLI uses with `lium config get api.api_key` |
| `pod_not_found` | 404 | No such pod on this account | `lium ps` lists yours; a deleted pod's entries stay in the account's event log, `GET /users/me/events` (API key or signed-in session) |
| `node_not_found` | 404 | The node left the marketplace or the id is wrong | Pick another from `lium ls` (UUID or the `huid` it prints) |
| `template_not_found` | 404 | No such template, or a private one of another account | `lium templates` |
| `not_found` | 404 | Anything else | Check the id |
| `node_rent_in_progress` | 400/409 | Another rental is starting on this node | Retry in ~30 s or pick another node |
| `node_not_verified` | 400 | The validator has not passed this node yet | Choose a node `lium ls` marks available |
| `node_unavailable` | 400 | The node was rented or went offline between listing and renting, or has no free GPU | Pick another node or retry in a minute |
| `node_paused_by_provider` | 409 | The provider paused new rentals on this node | Pick another node |
| `provider_banned` | 409 | The provider is banned | Pick a node from another provider |
| `gpu_count_invalid` | 400 | The requested GPU count is not available, is below the node's minimum, or a whole-node rental is required | `lium ls --format json` shows `gpu_count` per node |
| `gpu_split_not_allowed` | 400 | This node cannot be split | Rent all of its GPUs or pick a node that allows splitting |
| `template_invalid` | 400 | The template is not yours, or a one-time/temporary template is being reused | Use a template you own or an official one |
| `ttl_invalid` | 400 | Termination outside 1–720 hours | Fix the value |
| `pod_state_conflict` | 400 | The pod is not in a state that allows this (already deleting, not running, stuck pending) | `lium ps` shows its status; wait or delete it |
| `bad_request` | 400 | Refused for the reason in `message` | Fix the input |
| `validation_error` | 422 | A field failed validation | Fix the fields listed in `validation_errors` |
| `idempotency_key_invalid` | 422 | `POST /executors/{executor_uuid}/rent` with an `Idempotency-Key` header that is not 1–255 letters, digits, `-`, `_`, `.` or `:` | Send a well-formed key (a UUID is fine) or omit the header |
| `idempotency_key_reused` | 422 | The same `Idempotency-Key` was sent within 24 h with a different request body | Use a new key for a new request; repeat the original request unchanged to get its first answer again (`Idempotent-Replayed: true`) |
| `idempotency_in_progress` | 409 | The first request with this `Idempotency-Key` is still running | Retry after the `Retry-After` seconds (1) with the same key to receive its answer |
| `conflict` | 409 | The resource changed under you | Re-read and retry |
| `no_executor_matches_spec` | 409 | `POST /executors/rent-by-spec`: no node satisfies the spec; the body names the first constraint that left no candidate and the best value the remaining nodes offered for it | Relax that constraint (or raise `max_price_per_gpu_hour`); `lium ls` shows what is available now |
| `method_not_allowed` | 405 | The path exists, but not for this HTTP method | `GET /api/openapi.json` lists the methods per path |
| `rate_limited` | 429 | Too many requests from this IP | Retry after a minute |
| `client_error` | other 4xx | Refused for the reason in `message` | Fix the request; `status_code` says which rule |
| `upstream_unavailable` | 502–504 | A dependency (validator, payment provider) did not answer | Retry in a minute; quote the `request_id` if it persists |
| `internal_error` | 500 | Unhandled error | Retry once; quote the `request_id` to support |

A few endpoints send their own, upper-case codes in the same field (for example `BACKUP_STORAGE_BUSY`, `POD_NOT_FOUND` on the Discord support routes); they are documented on those endpoints.

## Routes with the older body

The `/volumes`, `/bookmark` and `/machine-requests` handlers catch their own errors and answer `{"success": false, "message": "…"}` with the HTTP status and no `error` object. Key on the status there. `/backup-configs` and `/backup-logs` re-raise their 4xx (so those carry the `error` object) and answer the flat body only on an unexpected 500. `volume_not_found` is sent by `GET /volumes/{volume_id}/current-stats` (404, through the global handler); the five CRUD handlers in `routes/volume.py` answer the flat body.

## In the SDK and the CLI

Coming with lium#190 (not in CLI 0.0.45, which prints the message only): every SDK exception carries the three fields:

```python
from lium.sdk import Lium, LiumError

try:
    Lium().up(executor_id="…")
except LiumError as e:
    if e.code == "insufficient_balance":
        ...  # top up and retry
    elif e.code in ("node_unavailable", "node_rent_in_progress"):
        ...  # pick another node
    else:
        raise  # e.hint says what to do; e.request_id is what to quote
```

The CLI prints the hint and the request id under the error and exits non-zero:

```
$ lium up brave-wolf-26
Node brave-wolf-26 could not be rented: API error 400: Node is not available. Run 'lium ps' to check whether a pod was created. Run 'lium ls --format json' for the nodes rentable now.
The node was rented or went offline between listing and renting; pick another (`lium ls`) or retry in a minute.
request_id: 0fd17bdf43884d48af88bca99c884936
```

On the commands that take `--json` (`lium exec`, `lium balance`, `lium describe`, …; `lium up` has no `--json`) the envelope on stderr carries the server's code: `{"ok": false, "error": {"code": "node_unavailable", "message": "…", "hint": "…", "exit_code": 3}, "data": {"request_id": "…"}}`.
