Errors
Every error returns a JSON body with a stable machine code and a human
message, plus an appropriate HTTP status:
{ "code": "quota_exceeded", "message": "concurrent sandbox limit reached" }Branch on code — it is stable across releases — not on the message text or the
raw HTTP status alone.
API codes
Section titled “API codes”Returned by the API for auth, tenancy, and quota. All are fail-closed: a missing config or the auth store being unavailable denies rather than defaults open.
| Status | code | Meaning & what to do |
|---|---|---|
400 | bad_request | Malformed JSON or invalid fields. Fix the body. |
401 | unauthorized | Missing/malformed Authorization, unknown key id, wrong secret, or a revoked key. Check or recreate the key. |
402 | insufficient_credits | Your prepaid balance is exhausted. Top up (or enable auto top-up) in Billing. |
403 | forbidden | Valid key, but the resource isn't yours — a template your tenant doesn't own, or another tenant's sandbox. |
403 | demo_tier_limit | A Demo key hit a demo boundary (concurrency, width, or a Pro-only capability like baking or connect-from-outside). The message says which; upgrading lifts it. |
404 | not_found | Unknown or reaped sandbox id, or one not visible to your tenant. Create a new one. |
409 | template_in_use | The template still backs live sandboxes — delete those first. |
409 | port_not_exposed | connect-url (or connect) targeted a port the template doesn't expose. Bake the port into the template's ready-state ports; the SDK raises GatewayError with this code. |
429 | rate_limited | Per-key request rate exceeded. Honor Retry-After and back off. |
429 | quota_exceeded | At your tier's concurrent-sandbox cap. Suspend/delete one, or upgrade. |
502 | upstream_error | The request reached the sandbox but the call failed upstream. Retry with backoff. |
Sandbox codes
Section titled “Sandbox codes”Returned by the sandbox for sandbox-scoped operations.
| Status | code | Meaning & what to do |
|---|---|---|
409 | busy | The sandbox has an in-flight exec — you can't fork or re-exec until it finishes. Retry after it completes. |
410 | session_gone | The sandbox was destroyed/reaped mid-request. Permanent — do not retry; create a new one. |
413 | (payload) | The request body or a file exceeded the size cap. Shrink it or stage via files. |
503 | at_capacity | The platform shed the request before doing any work (no side effect). Safe to retry with backoff. |
503 | draining | The platform is briefly at capacity or cycling capacity; no work was admitted. Safe to retry. |
503 | session_bringup_failed | The fork itself failed, leaving no live sandbox. Safe to retry. |
Retry guidance
Section titled “Retry guidance”- Idempotent requests (
GET,DELETE) are always safe to retry. - A non-idempotent request (a create/exec/fork
POST) is only safe to retry when the platform proves no side effect committed — codesat_capacity,draining,session_bringup_failed, or a connection refused before the request was sent. Any other ambiguous 5xx should not be blindly re-sent, or you risk double-forking. 429responses carryRetry-After; use exponential backoff with jitter so a shed group doesn't re-arrive as a thundering herd.
The collimate-rl SDK implements all of this — bounded retries,
full-jitter backoff, and side-effect-safe idempotency — so you rarely need to
hand-roll it.