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

# Errors and limits

> Every error code the robot and LLM routes return, with its HTTP status and what to do.

An error answer has a JSON body with one `error` object:

```json theme={null}
{
  "error": {
    "message": "Session 7c9e6679 at step 2 needs 12 image parts (4 per camera); got 3.",
    "type": "invalid_request_error",
    "param": "messages",
    "code": "image_count_mismatch"
  }
}
```

Branch on `code`.
`type` is coarse, and `param` is present only when one request field is at fault.
`message` is for people and can change.

## Every route

| HTTP | Code | Meaning | What to do |
| - | - | - | - |
| 401 | `missing_api_key` | The request has no Bearer key. | Send `Authorization: Bearer` with your key. |
| 401 | `invalid_api_key` | The key is wrong or revoked. | Run `abstract status`, then `abstract login` if it fails. Do not retry with the same key. |
| 402 | `payment_required` | The account has no payment method and no credit. | Buy credit in [Platform Billing](https://platform.abstract.inc/billing). |
| 402 | `insufficient_credit` | Available prepaid credit cannot cover the request's maximum cost. Nothing ran. | Buy credit in [Platform Billing](https://platform.abstract.inc/billing). A saved card alone does not fund usage. |
| 429 | `spend_limit_exceeded` | The request would cross the account's monthly spend limit. Nothing ran. | Tell the account owner. Do not retry. |
| 503 | `auth_unavailable` | Key verification is down. | Retry with backoff. |
| 503 | `billing_unavailable` | Billing is down. | Retry with backoff. |
| 503 | `metering_unavailable` | Usage metering is down. Nothing ran. | Retry with backoff. |
| 404 | `not_found` | The route does not exist. | Check the method and the path. |

## Robot models over HTTP

These codes come from `POST /v1/chat/completions`.

| HTTP | Code | Meaning | What to do |
| - | - | - | - |
| 400 | `invalid_body` | The body is not a JSON object. | Send a JSON object. |
| 400 | `model_missing` | `model` is missing or empty. | Send a robot model id. |
| 400 | `model_not_found` | `model` is not a served robot model. | Use an id from [Robot models](/robots/models). |
| 400 | `messages_missing` | `messages` is not a non-empty array. | Send at least one user message. |
| 400 | `no_user_message` | `messages` has no user message. | Add a user message. |
| 400 | `invalid_content` | The user message's `content` is not a string or an array of parts. | Send an array of parts. |
| 400 | `invalid_content_part` | A part is not a `text` or `image_url` object, or its text is not a string. | Fix the part that `param` names. |
| 400 | `invalid_image_url` | An image URL is not a string or is not valid base64. | Send `data:image/jpeg;base64,` and valid base64. |
| 400 | `unsupported_image_format` | An image is not a `data:image/jpeg;base64,` URL, or its bytes are not a JPEG. | Encode frames as JPEG. |
| 400 | `instruction_missing` | Image parts arrived without a text part. | Add the task instruction as a text part. |
| 400 | `invalid_parameter` | `stream` or `handoff` is not a boolean. | Send `true` or `false`. |
| 400 | `handoff_unavailable` | `handoff` is `true`. | Send `false` or omit it. See [Handoff](/robots/handoff). |
| 400 | `session_missing` | A stateful model got image parts without `session_id`. | Send a `session_id`. |
| 400 | `invalid_session_id` | `session_id` is empty, too long, not printable, or has surrounding whitespace. | Send at most 128 printable characters, for example a UUID. |
| 400 | `state_missing` | Image parts arrived without `robot_state`. | Send `robot_state`. |
| 400 | `state_shape_mismatch` | A `robot_state` array has the wrong length or a value that is not a finite number. | Match the model's table in [Robot models](/robots/models). |
| 400 | `action_horizon_invalid` | `action_horizon` is not an integer. | Send an integer. |
| 400 | `action_horizon_out_of_range` | `action_horizon` is below 1 or above the model's rows per chunk. | Send a value in range, or omit it. |
| 400 | `num_inference_steps_invalid` | `num_inference_steps` is not an integer. | Send an integer, or omit it. |
| 400 | `num_inference_steps_out_of_range` | `num_inference_steps` is outside 1 to 4. | Send a value in range, or omit it. |
| 400 | `image_count_mismatch` | The number of image parts does not match the model and the session step. | Send the frames per camera that the model's table and `next_frames_per_camera` name. |
| 400 | `instruction_changed` | The instruction differs from the session's first request. | Keep the instruction, or start a new session. |
| 400 | `invalid_image` | The model cannot use a frame or a state value: an undecodable JPEG, an edge over 4096 pixels, a gripper outside 0 to 1, or a degenerate rotation. | Fix the value the message names. |
| 409 | `idempotency_conflict` | Observation id was used with different content. | Preserve the original id and payload for a retry. |
| 409 | `session_step_conflict` | The expected step differs from the next session step. | Reconcile the previous observation's result before advancing. |
| 409/503 | `observation_outcome_unknown` | The observation is pending or its result was not retained. | Retry only the same id; never bypass the claim with a new id. |
| 400 | `invalid_expected_step` | Expected step is not a nonnegative integer. | Start at zero and advance only after accepting the previous result. |
| 409 | `session_busy` | Another request for this `session_id` is still running. | Wait for it. Send one request per session at a time. |
| 410 | `session_expired` | The session expired or its state is gone. | Start a new session with a new `session_id` and 1 frame per camera. |
| 413 | `image_too_large` | An image is over 262,144 bytes. | Lower the JPEG quality or the size. |
| 413 | `request_too_large` | The body is over 8 MiB. | Send fewer or smaller frames. |
| 429 | `no_replica_free` | No capacity is free to start a new session. | Wait for the `Retry-After` seconds, then retry. |
| 500 | `model_failure` | The model failed. | Retry. If the next request returns 410, start a new session. |
| 502, 503 | `engine_unavailable` | The model service is unreachable. | Retry with backoff. |

With `"stream": true`, a failure after the stream starts keeps HTTP status 200.
It arrives as a `data:` event with an `error` object and `finish_reason` `"error"`.
Its `code` is one of the codes above, for example `model_failure` or `session_expired`.

## Robot models over WebSocket

The handshake refusals are in [Sessions over WebSocket](/robots/sessions#connect).
On an open socket, an error arrives as a `{"type":"error"}` message with a `closing` flag.

With `closing: false`, the rejected observation does not advance the session. Other queued
observations may still run. An observation error carries its `observation_id`; frame and
session-control errors do not. Fix the message and send it again:

| Code | Meaning |
| - | - |
| `invalid_json`, `invalid_body`, `type_missing`, `unknown_type` | The text message is not a JSON object with a known `type`. |
| `message_too_large` | A text message is over 65,536 characters. |
| `session_open` | The socket already has a session. Open a new socket for a new episode. |
| `session_missing` | A frame or observation arrived before `session.open`. |
| `instruction_missing` | `session.open` has no instruction. |
| `handoff_unavailable` | `handoff` is `true` in `session.open`. |
| `invalid_parameter` | A boolean field is not a boolean. |
| `invalid_frame` | A binary message is shorter than one byte plus a JPEG header. |
| `invalid_camera` | The camera index byte is out of range. |
| `image_too_large` | A frame's JPEG is over 262,144 bytes. |
| `unsupported_image_format` | A frame is not a JPEG. |
| `image_count_mismatch` | A camera has the wrong number of frames for this observation. The server dropped the pending frames. |
| `state_missing`, `state_shape_mismatch` | `robot_state` is missing or has the wrong shape. |
| `action_horizon_invalid`, `action_horizon_out_of_range`, `num_inference_steps_invalid`, `num_inference_steps_out_of_range` | A control value is wrong. |
| `invalid_image` | The model cannot use a frame or a state value. On the first observation of a session, this closes the socket. |

With `closing: true`, the socket closes next:

| Code | Close code | What to do |
| - | - | - |
| `model_missing`, `model_not_found` | 1008 | Open a socket with a served robot model id. |
| `session_busy` | 1008 | Another connection holds this `session_id`. Use a new one. |
| `no_replica_free` | 1008 | Wait, then open a new socket. |
| `session_expired` | 1008 | Open a new socket. |
| `spend_limit_exceeded` | 1008 | Tell the account owner. Do not retry. |
| `insufficient_credit` | 1008 | Buy API credit in Platform before opening a new socket. |
| `metering_unavailable` | 1008 | Retry with backoff on a new socket. |
| `model_failure` | 1011 | Retry on a new socket. |
| `invalid_observation_id` | 1008 | Use 1–128 characters from `A-Z a-z 0-9 . _ : -`, or omit the id. |
| `idempotency_conflict` | 1008 | The id was used with different content. Preserve the original controls and frames when retrying. |
| `observation_outcome_unknown` | 1008 | Retry only the same id and session; the service will not execute it again. |
| `too_many_observations` | 1008 | Keep at most eight observations outstanding; wait for replies before submitting more. |
| `message_too_large` | 1008 | A control message exceeds 65,536 characters, observation frames exceed 16 MiB, or pending socket ingress exceeds 16 MiB or 128 messages. Reduce the observation and wait for replies before sending more. |
| `engine_protocol_error` | 1011 | The service could not correlate an action result. Open a new socket; report persistent failures. |

## LLM models

These codes come from `POST /v1/responses` and `GET /v1/responses/{id}`.

| HTTP | Code | Meaning | What to do |
| - | - | - | - |
| 422 | `invalid_request` | The body is not a JSON object, the model is unknown, or a field is not supported. | Fix the field the message names. See [Responses API](/llm/responses). |
| 413 | `invalid_request` | The body is over 2 MiB. | Send a smaller input. |
| 422 | `invalid_idempotency_key` | `Idempotency-Key` breaks its format. | Send 1 to 200 characters from `A-Z a-z 0-9 _ . / : -`. |
| 409 | `idempotency_conflict` | The key was used with a different body. | Use a new key for a new request. |
| 409 | `idempotency_replay_terminal` | The earlier request with this key ended without a response. | Use a new key to retry. |
| 409 | `submission_outcome_unknown` | The earlier request with this key may still be submitting. | Wait, then repeat with the same key. A new key can start a second job. |
| 4xx | `upstream_rejected` | The model provider rejected the request. | Check the request fields. |
| 5xx | `upstream_unavailable` | The model provider or the stored response is unavailable. | Retry with the same `Idempotency-Key`, or retry the GET. |
| 502 | `invalid_upstream_response` | The provider answered something invalid. | Retry with the same `Idempotency-Key`, or retry the GET. |
| 503 | `unconfigured` | LLM inference is not configured. | Retry later. |
| 422 | `invalid_id` | The response id in the path breaks its format. | Use the `id` from the POST answer. |
| 404 | `not_found` | No response with this id belongs to your account. | Use an `id` your account created. |

A buffered stream can end with an `error` event:

| Code | Meaning | What to do |
| - | - | - |
| `stream_timeout` | The connection reached 30 minutes. The job goes on. | Poll `GET /v1/responses/{id}`. |
| `stream_unavailable` | The stream could not read the job. The job goes on. | Poll `GET /v1/responses/{id}`, or repeat the POST with the same `Idempotency-Key`. |

## Robot runs

These codes come from `/v1/robot/runs`.

| HTTP | Code | Meaning | What to do |
| - | - | - | - |
| 404 | `run_not_found` | No completed run by that name in your account. | List your runs, or complete this one. |
| 404 | `file_not_found` | The run has no file at that path. | Check the path. |
| 404 | `upload_not_found` | No multipart upload with that `upload_id` for this file. | Start a new upload. |
| 409 | `upload_in_progress` | Another unfinished upload owns this path, or the run is not ready to complete. | Resume or abort that upload, then retry. |
| 429 | `too_many_uploads` | This run already has 16 unfinished uploads. | Finish or abort one before starting another. |
| 503 | `storage_busy` | The run has too many concurrent storage requests. | Retry with backoff. |
| 409 | `run_incomplete` | A listed file is missing or has another size, or a stored file is not listed. | Upload or delete what the message names, then complete again. |
| 411 | `length_required` | An upload has no `Content-Length`. | Send it. |
| 413 | `request_too_large` | A file or part is over 64 MiB, or a JSON body over 1 MiB. | Upload the file in parts. |
| 413 | `run_too_large` | The run would hold over 2 GiB or 2,000 files. | Split the run. |
| 413 | `storage_quota_exceeded` | Committed files plus upload reservations would exceed the quota. A staged upload is preserved for retry or abort. | Delete runs you no longer need (`GET /v1/robot/runs/usage` shows stored and reserved bytes), abort unfinished uploads, or ask us for more room. Publication needs room for a temporary additional copy. |
| 416 | `invalid_range` | The `Range` cannot be served. | Ask for a range inside the file. |
| 422 | `checksum_mismatch` | The body, or the assembled file, is not the SHA-256 you sent. Nothing was kept. | Upload it again. |
| 422 | `invalid_request` | A robot or run name, a path, the query or the body breaks its format. | Fix it; the message says which. |
| 503 | `storage_unavailable` | Run storage is down. | Retry with backoff. |

## Limits

| Limit | Value |
| - | - |
| Robot request body (`POST /v1/chat/completions`) | 8 MiB |
| LLM request body (`POST /v1/responses`) | 2 MiB |
| One JPEG frame, before base64 | 262,144 bytes |
| Frame edge | 4096 pixels |
| `session_id` | 128 characters |
| WebSocket text message | 65,536 characters |
| WebSocket observations outstanding | 8 |
| `observation_id` | 128 characters |
| Idle session | 60 seconds |
| Buffered stream connection | 30 minutes |
| `max_output_tokens` | 65,536 |
| `Idempotency-Key` | 200 characters |
| Robot run file in one request, or one part | 64 MiB |
| Robot run | 2 GiB, 2,000 files |
| Robot runs per account | 100 GiB |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.