error object:
{
"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"
}
}
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. |
| 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. |
| 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 fromPOST /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. |
| 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. |
| 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. |
| 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. |
"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. 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. |
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 fromPOST /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. |
| 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. |
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 |