Error codes
Every error the API returns — one shape, a fixed code list, and explicit guidance on what is retryable.
Every non-2xx response has the same shape, and every code on this page is in
the enum in /openapi.json — there are no undocumented
errors.
{
"error": {
"code": "budget_exceeded",
"message": "Run halted at max_budget_usd=500 after 74 episodes.",
"run_id": "run_8843",
"doc_url": "https://roborama.com/docs/errors/#budget_exceeded"
}
}
run_id appears when the error concerns a specific run. SDKs raise these as
typed exceptions carrying the same fields.
Auth and access
| Code | HTTP | Meaning | Retry? |
|---|---|---|---|
invalid_api_key | 401 | key missing, malformed, or revoked; live keys use the rbr_live_ prefix | no — fix the key |
live_key_on_sandbox | 403 | a rbr_live_ key was sent to the test-mode base URL (test mode) | no — use a rbr_test_ key |
insufficient_scope | 403 | key is valid but lacks the needed scope, e.g. runs:write | no — issue a key with the scope |
not_found | 404 | no such run, robot, environment, suite, or stream | no |
publish_forbidden | 403 | publish="leaderboard" requested without leaderboard opt-in on the account | no — opt in first |
rate_limited | 429 | request rate exceeded; Retry-After header is set | yes — after the header's delay |
method_not_allowed | 405 | the path exists but not for this HTTP method; the full surface is in /openapi.json | no — fix the method |
Validation (submission rejected, nothing ran)
These fire before any motor moves; a rejected submission costs nothing.
| Code | HTTP | Meaning | Retry? |
|---|---|---|---|
request_invalid | 400 | the body is malformed JSON, isn't an object, or a field has the wrong type; nothing was created — a parse failure is never a silent 201 | no — fix the request |
contract_validation_failed | 422 | declared action_space or observation_contract doesn't match the pinned robot and scene (policies) | no — fix the declaration or the pin |
firmware_pin_unavailable | 422 | requested model@firmware isn't installed on any cell; check robots.list() firmwares | no — pin an available revision |
environment_unavailable | 422 | environment id or revision not in the catalogue, or offline for rebuild | no — check environments.list() |
task_unknown | 422 | task id or version not recognized, e.g. a typo in load_dishwasher@v2 | no |
suite_unknown | 422 | suite id or revision not recognized; check suites.list() | no |
task_frozen | 409 | the task revision is frozen — immutable by construction; amendments create the next revision from a new draft (tasks) | no — draft the next revision |
robot_unknown | 422 | robot id not in the catalogue; check robots.list() | no |
predicate_unbindable | 422 | a success predicate names an instrument the target environment class doesn't carry (tasks) | no — change the predicate or the environment |
checkpoint_source_unauthorized | 403 | the checkpoint source requires credentials, e.g. a private Hugging Face repo without a valid hf_token | no — supply credentials |
scenario_format_invalid | 422 | verify() scenarios parse as none of: PolaRiS/Isaac/world-model exports, layout-replay specs | no — fix the file |
episodes_invalid | 422 | episodes is neither a positive int nor a well-formed auto(ci=..., moe=...) | no |
quote_expired | 409 | submission referenced a quote past its validity window | yes — re-quote and resubmit |
Runtime (the run itself)
| Code | HTTP | Meaning | Retry? |
|---|---|---|---|
budget_exceeded | 402 | max_budget_usd hard stop fired mid-run; partial results kept | resubmit with a higher cap if the interval is too wide |
monthly_budget_exceeded | 402 | account-level hard budget reached; submissions rejected, in-flight runs halted | no — raise the budget |
capacity_exceeded | 409 | no qualified cell can absorb the submission within its window — robot in maintenance, or episode count beyond fleet capacity | yes — smaller run, or wait |
queue_timeout | 408 | job couldn't be scheduled within its priority tier's window | yes — resubmit, or use burst |
policy_endpoint_timeout | 408 | endpoint policy persistently exceeded latency_budget_ms; run halted | fix the endpoint, then resubmit |
policy_deadline_missed | 408 | one endpoint-policy step blew its declared latency_budget_ms; the episode counts as a policy fault (billable) | fix the endpoint |
estop_triggered | 409 | hardware or safety emergency stop during the run; also emitted as the estop.triggered webhook | contact support if unexpected |
retention_expired | 410 | artifacts past their data.retention window (or purged) | no — the data is gone, by design |
Live runtime
The live runtime answers in the same shape. These codes describe its durable execution model; the sandbox never emits them.
| Code | HTTP | Meaning | Retry? |
|---|---|---|---|
validation_failed | 422 | the request was well formed but its content was rejected before anything ran; the message names the field or rule | no — fix the request |
idempotency_conflict | 409 | the Idempotency-Key was already used with different content; the original run or operation is unchanged | no — use a new key for new intent |
delivery_uncertain | 409 | an operator operation was sent but its acknowledgement was lost; nothing is replayed | inspect the recorded operation before any new intent |
cell_state_conflict | 409 | the cell is active, initializing, armed or otherwise not in the state the operation needs | yes — after the cell settles |
resource_immutable | 409 | a catalogue version already exists with different content; versions never change | no — register a new version |
artifacts_pending | 409 | the run is terminal but its evidence is still transferring to the archive | yes — poll artifact_status |
service_unavailable | 503 | a dependency (database, archive, agent, key resolver) is unreachable; nothing was granted or denied | yes — retry with backoff |
Facility incidents (never billed)
These codes mark facility faults — our hardware, not your policy. The full policy, including how detection is instrumented and how statistics stay honest, lives on Facility incidents & credits. The short version: you pay for physics, not for our hardware.
| Code | HTTP | Meaning | Retry? |
|---|---|---|---|
cell_fault | 409 | a facility fault (hardware failure, calibration drift, reset-fixture jam, power/network) invalidated an episode; it is excluded from n, requeued or credited, and never billed | no action — handled automatically |
run_requeued_facility | 409 | facility-fault episodes were requeued on the same or an equivalent qualified cell at no charge; also emitted as the run.requeued webhook | no action needed |
run_credited_facility | 409 | facility-fault episodes couldn't be requeued within the run's window; their cost is credited and the achieved CI is reported with the shortfall flagged | resubmit later if you need the planned n |
Every code on this page — including these — can be forced in
test mode with the X-Roborama-Force header, so your
error handling can be exercised in CI before a single real episode runs.
Handling the important one
budget_exceeded is the code every integration should handle deliberately,
because it carries a partial result with honest statistics — n and the
interval describe the episodes that ran.
import roborama # reads ROBORAMA_API_KEY from the environment
try:
run = roborama.run(
robot="g1-edu-pro@fw2.3",
environment="kitchen-std@v1.2",
task="pick_place@v1",
episodes=600,
max_budget_usd=500, # deliberately too small for 600 episodes
)
print(run.result())
except roborama.BudgetExceeded as err:
# budget_exceeded: the hard stop fired mid-run; partial results are kept
print(err.code, err.run_id)
partial = err.partial_result
print(partial.n, partial.success_rate, partial.ci95)