error object:
{
"error": {
"message": "Invalid API key.",
"type": "invalid_request_error",
"code": "invalid_api_key",
"retryable": false,
"retry_after_s": null,
"request_id": "req_5dd569e1ce524fc4b1518e681c57e75a"
}
}
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.
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. |
| 402 | insufficient_credit | Available prepaid credit cannot cover the request’s maximum cost. Nothing ran. | Buy credit in Platform 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. |
| 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 fromPOST /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:
{
"status": { "code": "model_overloaded", "reason": "…", "retryable": true, "retry_after_s": 1 },
"error": { "code": "model_overloaded", "message": "…", "retryable": true, "retry_after_s": 1 }
}
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. |
| 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. |
| 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. |
| 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. 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 fromPOST /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 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. |
| 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 |