> ## Documentation Index
> Fetch the complete documentation index at: https://abstract-pbc.mintlify.site/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={"theme":"css-variables"}
{
	"error": {
		"message": "Invalid API key.",
		"type": "invalid_request_error",
		"code": "invalid_api_key",
		"retryable": false,
		"retry_after_s": null,
		"request_id": "req_5dd569e1ce524fc4b1518e681c57e75a"
	}
}
```

Branch on `code`.
`type` is a broad category, and `param` is present only when one request field is at fault.
`message` is for people and can change.
`retryable` says whether this same request may be sent again: the route's own answer when it gives one, otherwise `true` for 408, 429 and 503.
`retry_after_s` repeats the `Retry-After` header in seconds, or is `null`.
`request_id` repeats the answer's `Request-Id` header. Quote it when you report a problem.
See [Versions and request ids](https://abstract-pbc.mintlify.site/api-reference/introduction.md#versions-and-request-ids).

## Every route

| HTTP | Code | Meaning | What to do |
| - | - | - | - |
| 400 | `unsupported_api_version` | `Abstract-Version` names a version this API does not answer. Nothing ran. | Send `2026-10-27`, or leave the header out. |
| 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. | Fix or unset `ABSTRACT_API_KEY` if it is set; else run `abstract logout --local`, then `abstract login`. Do not retry 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. |
| 429 | `spend_blocked` | A request this month cost more than it reserved, so the account accepts no new requests until the next calendar month. Nothing ran. | Tell the account owner. Do not retry before next month. |
| 429 | `pass_limit_reached` | The account's Abstract Pass limit is used up, and extra usage is off or has no credit to draw on. Nothing ran. | Wait until `error.resets_at`, or buy credit in [Platform Billing](https://platform.abstract.inc/billing). |
| 429 | `rate_limit_exceeded` | Too many requests from this IP reached the edge admission limit. Nothing ran. | Wait for the `Retry-After` seconds, then retry the same request. |
| 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. |
| 503 | `admission_unavailable` | Edge admission is down. Nothing ran. | Retry with backoff. |
| 404 | `not_found` | The route does not exist. | Check the method and the path. |

## Robot models

These codes come from `POST /v1/get_action` and `POST /v1/end_session`.
A robot error keeps its HTTP status and carries a `status` object beside the `error` object, with the same `code`:

```json theme={"theme":"css-variables"}
{
	"status": { "code": "model_overloaded", "reason": "…", "retryable": true, "retry_after_s": 1 },
	"error": { "code": "model_overloaded", "message": "…", "retryable": true, "retry_after_s": 1 }
}
```

Both carry the same `retryable`, `retry_after_s` and `request_id`.

| 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](https://abstract-pbc.mintlify.site/robots/models.md). |
| 400 | `invalid_prompt` | `observation.prompt` is missing or empty. | Send the task instruction. |
| 400 | `instruction_too_long` | `observation.prompt` is over 512 characters. | Shorten the task instruction. |
| 400 | `instruction_changed` | The instruction differs from the session's first request. | Keep the instruction, or start a new session. |
| 400 | `invalid_image` | `observation.video` has missing or unexpected camera fields for the selected model, or a frame is not a base64 string. | Send the selected model's camera arrays of base64 JPEG frames. |
| 400 | `frame_count_mismatch` | The three camera windows have different frame counts. | Send the same count for each camera. |
| 400 | `image_count_mismatch` | The frames per camera do 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 | `invalid_image_url` | A frame is not valid base64. | Send each frame as base64 JPEG bytes. |
| 400 | `unsupported_image_format` | A frame's bytes are not a JPEG, or its `data:` prefix is not `data:image/jpeg;base64,`. | Encode frames as JPEG. |
| 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. |
| 400 | `state_shape_mismatch` | `observation.state` has missing or unsupported fields for the selected model, or a value has the wrong length. | Send the fields required by the selected model; see [Get action](https://abstract-pbc.mintlify.site/robots/get-action.md#request). |
| 400 | `pose_unreachable` | A grasp-site pose in `observation.state` is out of a YAM arm's reach, on the YAM models except `microsoft/rho`. | Send each pose in its arm's base frame, inside the YAM's workspace. |
| 400 | `invalid_session_id` | `session_id` is not `null` or 1 to 128 printable characters. | Send the id the last answer carried, or `null`. |
| 400 | `invalid_reset` | `reset` is not a boolean. | Send `true` or `false`. |
| 400 | `invalid_action_horizon` | `action_horizon` is not a positive safe integer. | Send an integer from 1 to the model's horizon, or omit it. |
| 400 | `action_horizon_out_of_range` | `action_horizon` is above the model's rows per chunk. | Send a value in range, or omit it. |
| 400 | `invalid_expected_step` | Expected step is not a nonnegative integer. | Start at zero and advance only after accepting the previous result. |
| 400 | `invalid_parameter` | `handoff` is not a boolean, or `scaling` is not an object. | Fix the field `param` names. |
| 400 | `invalid_scaling` | The model does not support the `scaling` asked for: a seed on a model other than MolmoAct2 and π0.5. | Omit `scaling`, or send only what the model supports. |
| 400 | `handoff_unavailable` | `handoff` is `true`. | Send `false` or omit it. See [Handoff](https://abstract-pbc.mintlify.site/robots/handoff.md). |
| 403 | `flux_content_blocked` | The FLUX instruction matches a prohibited-use prompt filter. No inference runs and nothing is billed. | Review the task against [FLUX content filters](https://abstract-pbc.mintlify.site/robots/get-action.md#flux-content-filters). Do not retry the unchanged request or try to bypass the filter. |
| 408 | `observation_expired` | The observation waited more than one second for the model and was dropped. Nothing ran, and the session stays at its step. | Retry with fresh frames and a new `observation_id`. |
| 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. | Resume from the `error.next_step` the answer carries, after reconciling the previous observation's result. |
| 409 | `observation_in_progress` | The observation is still running. | Wait for `Retry-After`, then retry the same id and body. |
| 409/503 | `observation_lost` | The observation has no retrievable answer and will not execute again under this id. | Do not execute a missing chunk. Send the next observation under a new id; a step conflict names the step to resume from. |
| 409/503 | `observation_outcome_unknown` | A legacy gateway cannot yet say whether the observation is pending or lost. | Retry the same id only. Do not send a new id until the outcome is known. |
| 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. |
| 429 | `model_overloaded` | The model is already running an observation from your account, or its queue is full. Nothing ran, and the session stays at its step. | Wait for the `Retry-After` seconds, then retry with fresh frames and a new `observation_id`. |
| 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. |
| 502 | `engine_protocol_error` | The model service answered no valid action chunk, finite targets or schedule. No chunk is billed. | Report the problem. Retrying under the same `observation_id` or `Idempotency-Key` returns the same refusal and does not run the model again. |
| 502 | `flux_output_blocked` | The FLUX reply fails the mandatory output filter, including native action, metadata or Panda/FER joint-position checks. No action rows are delivered and the chunk is not billed. | Report the request id. Do not execute a missing chunk or retry automatically; retrying under the same `observation_id` or `Idempotency-Key` returns the same refusal. |

## LLM models

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

| HTTP | Code | Meaning | What to do |
| - | - | - | - |
| 400 | `use_get_action` | `model` is a robot model id. Chat completions serves LLMs only. | Call [`POST /v1/get_action`](https://abstract-pbc.mintlify.site/robots/get-action) instead. |
| 422 | `invalid_request` | The body is not a JSON object, the model is unknown, a field is not supported, or an `image_url` is not a `data:` URL. | Fix the field the message names. See [LLM chat completions](https://abstract-pbc.mintlify.site/llm/chat-completions.md). |
| 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 an answer. | 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. |
| 429 | `no_replica_free` | Every replica serving the model is busy. Nothing was reserved. | Wait for the `Retry-After` seconds, then retry with the same `Idempotency-Key`. |
| 4xx | `upstream_rejected` | The model provider rejected the request. | Check the request fields. |
| 5xx | `upstream_unavailable` | No replica serves the model right now, or the model provider or the stored answer 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. |
| 502 | `upstream_failed` | The model did not complete the request. | Retry with a new `Idempotency-Key`. |
| 503 | `unconfigured` | LLM inference is not configured. | Retry later. |
| 422 | `invalid_id` | The completion id in the path breaks its format. | Use the `id` from the POST answer. |
| 404 | `not_found` | No completion with this id belongs to your account. | Use an `id` your account created. |

`GET /v1/chat/completions/{id}` reads a completion from a retired model back from its kept answer. One whose answer was never kept answers 404 `not_found`.

## Limits

| Limit | Value |
| - | - |
| Robot request body (`POST /v1/get_action`) | 8 MiB |
| LLM request body (`POST /v1/chat/completions`) | 2 MiB |
| One JPEG frame, before base64 | 262,144 bytes |
| Frame edge | 4096 pixels |
| Robot `observation.prompt` | 512 characters |
| Robot frames per camera | 1–9, as required by the selected model |
| `session_id` | 128 characters |
| `observation_id` | 128 characters |
| Idle session | 60 seconds |
| `max_completion_tokens` | 65,536 |
| `Idempotency-Key` | 200 characters |


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