Errors and exit codes
Every failure is attributed. An environment failure is ours: kept out of your result, retried once before hand-over, and not billed when our side can show it was ours. A task failure is yours: billed, because the environment worked.
Exit codes
| Exit | Meaning | Billing |
|---|---|---|
| 0 | Success | billed |
| 1 | Internal error on our side | not billed |
| 2 | Usage error, bad or missing arguments | not billed |
| 3 | Environment failure, the environment was at fault, not your command | not billed when our side shows it was ours |
| 4 | Task failure, your command exited non-zero | billed |
| 5 | Refused: the quote exceeds your spend cap | not billed |
| 6 | The snapshot is not verified | not billed |
| 7 | Missing or invalid API key | not billed |
| 8 | Not found | not billed |
| 9 | Unreachable | not billed |
| 10 | Rate limited | not billed |
| 11 | A --wait expired. Nothing was killed | unchanged |
| 12 | Partial, some environments did not start, or were stopped at the cap boundary | billed for what ran |
| 130 | Interrupted by the client | billed for what ran |
Every error answer from the API is a JSON body with code, message, fix, category, retryable (true when the same request, sent again unchanged, can succeed) and request_id (quote it to support).
env
Environment failure, ours. Not billed when our side can show it was ours (a sandbox that goes silent on its own is billed as used), retried once before hand-over when the run has a command, and never counted against your agent in a validity report.
| Code | Meaning | HTTP | Exit | Billed |
|---|---|---|---|---|
| fork_failed | The sandbox could not be started.Not billed. A run with a command is retried once automatically; otherwise re-run. If it repeats, re-verify the snapshot: `promigence snapshot verify {snapshot_id}`. | 502 | 3 | no |
| liveness_timeout | The sandbox did not answer its first command within {deadline_ms} ms.Not billed. A run with a command is retried once automatically; otherwise re-run the fork. If it keeps happening, tell support@promigence.ai the run id. | 502 | 3 | no |
| probe_warm_failed | The sandbox failed its readiness check.The sandbox was discarded, not billed. Re-run; if it keeps failing, re-verify the snapshot: `promigence snapshot verify {snapshot_id}`. | 502 | 3 | no |
| warm_ratio_failed | Verify cpu_ms {verify_cpu_ms} exceeds max(2 x {baseline_cpu_ms}, {baseline_cpu_ms} + 250).The fork was not warm; not billed. Re-run. If steady, re-verify the snapshot: `promigence snapshot verify {snapshot_id}`. | 502 | 3 | no |
| verify_each_failed | Verify_each run failed in the fork (exit {exit_code}, digest {digest}).Not billed. A run with a command is retried once automatically. If it repeats, the snapshot no longer reproduces: rebuild with `promigence snapshot create ...`. | 502 | 3 | no |
| toolchain_mismatch | Toolchain check `{argv}` in the fork gave digest {got}, the verified snapshot recorded {expected}.The fork's toolchain differs from the verified snapshot; not billed. Re-run; if it repeats, rebuild the snapshot. | 502 | 3 | no |
| guest_agent_lost | The sandbox stopped responding.Billed for the time used: nothing on our side explains it. Never retried automatically after hand-off. Replay: `promigence run replay {run_id} --sandbox {sandbox_id}`; if you think it was ours, tell support@promigence.ai the run id. | 502 | 3 | yes |
| oom_within_tier | The sandbox was killed by a memory fault on our side while under the tier's {tier_mib} MiB.Our fault, not yours: the work lost since your last snapshot of this sandbox is credited back, up to 24 hours (a sandbox under 24 hours with no snapshot is never charged). Replay: `promigence run replay {run_id} --sandbox {sandbox_id}`. | 502 | 3 | no |
| disk_full_within_tier | A storage fault on our side while the sandbox was under its {tier_disk_gib} GiB quota.Our fault, not yours: the work lost since your last snapshot of this sandbox is credited back, up to 24 hours (a sandbox under 24 hours with no snapshot is never charged). Replay: `promigence run replay {run_id} --sandbox {sandbox_id}`. | 502 | 3 | no |
| node_lost | The host running the sandbox became unreachable.Credited back: the work lost since your last snapshot of this sandbox, up to 24 hours (a sandbox under 24 hours with no snapshot is never charged). Replay: `promigence run replay {run_id}`. | 502 | 3 | no |
| layer_integrity | The environment differs from the verified snapshot at {path}.Not billed when the verified snapshot itself differs; when only this sandbox's copy changed, the time used is billed. Replay: `promigence run replay {run_id} --sandbox {sandbox_id}`. | 502 | 3 | no |
| egress_unavailable | The sandbox's network egress check failed.Not billed when we confirm the outage on our side; otherwise the time used is billed. Replay: `promigence run replay {run_id}`. | 502 | 3 | no |
| infra_timeout | An internal deadline ({deadline_ms} ms) fired before your timeout.Our deadline fired before the command returned. When the cause was ours (our service restarted), the work lost since your last snapshot of this sandbox is credited back, up to 24 hours; otherwise the time used is billed. Set `timeout_ms` for long commands. Replay: `promigence run replay {run_id} --sandbox {sandbox_id}`. | 504 | 3 | yes |
| pool_exhausted | No capacity free right now.Not billed. Nothing was free in time: start it again in a few minutes, or lower `--count` to need fewer at once. | 503 | 3 | no |
| quota_exhausted | {region} is at capacity right now.Not billed. Retry shortly, or lower `--count`. | 503 | 3 | no |
| region_exhausted | {region} has no free capacity right now.Not billed. Retry shortly, or lower `--count`. | 503 | 3 | no |
| builder_unavailable | New snapshots cannot be built right now.Not billed, and nothing was stored. Nothing here can build at all, so this is not a busy queue and does not clear itself by waiting, and a smaller run does not help. Snapshots you have already built still run, so re-use one (`promigence snapshot list`). If you asked for a `cpu_class`, drop it to build on any host; otherwise retry shortly, and tell support@promigence.ai the time if it keeps happening. | 503 | 3 | no |
| builder_busy | Snapshot builds are backed up; retry after {retry_after_s} s.Not billed, and nothing was stored. Other builds are ahead of this one: submit it again in {retry_after_s} s. Builds already accepted keep their place in the queue and finish on their own (`promigence snapshot status <build_id> --wait`). | 503 | 3 | no |
| overloaded | The API is at capacity; retry in {retry_after_s} s.Not billed, and nothing was started. Send the same request again in {retry_after_s} s; the SDKs and the CLI do this for you. | 503 | 3 | no |
| tier_unavailable | The {tier} tier ({vcpus} vCPU / {mem_gib} GiB) is not available here yet.Not billed. Use a smaller tier (`promigence snapshot create … --tier`; a fork runs on its snapshot's), or write to support@promigence.ai to have {tier} enabled for your account. | 503 | 3 | no |
| snapshot_unavailable | Snapshot {snapshot_id} is no longer available: the capacity that held it was retired before it was saved.Not billed, and nothing was started. It cannot be started again: take a new snapshot of a running sandbox (`promigence sandbox snapshot <sandbox_id>`) or build it again (`promigence snapshot create ...`), then remove this one: `promigence snapshot delete {snapshot_id}`. | 409 | 3 | no |
| capacity_limit | {requested} {tier} sandboxes at once is more than can run together; at most {max_at_once} at once.Not billed; nothing started. Run at most {max_at_once} at once (lower `--count`), or use a smaller tier. | 409 | 3 | no |
| registry_unavailable | `{image}` could not be resolved to a digest because its registry did not answer usably: {detail}.Not billed, and nothing was stored. Your image is not at fault: submit it again in {retry_after_s} s. Snapshots you have already built from it still run (`promigence snapshot list`). | 503 | 3 | no |
| network_policy_unsupported | This sandbox asked to reach only {allow}, and that could not be applied where it was placed.Nothing ran and nothing was billed: the sandbox was discarded rather than started without the restriction you asked for. Re-run; if it repeats, tell support@promigence.ai the run id. | 502 | 3 | yes |
| secrets_unavailable | Secrets cannot be used right now.Not billed, and nothing that names a secret was started. Runs, commands and terminals that name no secret are unaffected, and stored secrets can still be listed and deleted. Retry shortly; if it keeps happening, tell support@promigence.ai the time. | 503 | 3 | no |
| secret_binding_unsupported | A secret bound to a host could not be set up for this sandbox.Nothing ran, nothing was billed, and the value never entered the sandbox. Re-run; if it repeats, tell support@promigence.ai the run id. | 502 | 3 | yes |
| idempotency_key_in_use | A request with this Idempotency-Key is still running.Not billed; nothing new was started. Send the same request again in {retry_after_s} s for the first one's answer; the SDKs do this for you. | 409 | 3 | no |
task
Task failure, yours. Your command exited non-zero, timed out, or exceeded the tier. Billed, because the environment did its job.
| Code | Meaning | HTTP | Exit | Billed |
|---|---|---|---|---|
| nonzero_exit | Command exited {exit_code}.The environment was verified; the command failed. Inspect: `promigence exec output {exec_id}`. | 422 | 4 | no |
| grade_not_passed | The task completed and its grade did not pass (the grader exited {exit_code}).Your grader failed the work; the task's command succeeded. | 422 | 4 | no |
| task_timeout | Command exceeded timeout_ms={timeout_ms}.Raise `--timeout` (billed per second used, so a longer timeout only costs what runs) or shorten the task. | 422 | 4 | yes |
| oom_over_tier | The process was killed for exceeding the tier's {tier_mib} MiB of memory.Rebuild the snapshot on a larger tier (`promigence snapshot create … --tier medium|large`; see `promigence agent-guide --section cost`) or reduce the task's memory. | 422 | 4 | no |
| disk_over_tier | The sandbox ran out of its {tier_disk_gib} GiB of disk.Pick a larger tier or write less; the disk is sized to the tier quota. | 422 | 4 | no |
| shutdown_by_task | The sandbox shut itself down ({kind}).Billed as the task's outcome: a command in the sandbox rebooted, powered off, halted or crashed it, or ended process 1. Keep the task from doing that; if nothing in it should have, check what the agent ran. | 422 | 4 | yes |
| modified_by_task | `{missing}` was removed or changed inside the sandbox; the snapshot's copy is intact.Billed as the task's outcome: the sandbox started verified and something it ran deleted or changed `{path}`. Keep the task from modifying verified paths, or recreate what it needs first. | 422 | 4 | yes |
cap
Spend cap. Either the run was refused before it started, or a sandbox was stopped when its spend reached your cap (its seconds are billed).
| Code | Meaning | HTTP | Exit | Billed |
|---|---|---|---|---|
| cap_killed | Sandbox killed when its accrued spend reached the spend cap ({spend_cap}).The seconds it ran are billed, up to the cap. Raise `--cap` or `PATCH /sandboxes/:id` earlier. | 402 | 12 | yes |
| cap_exceeded | Quote {max_cost} exceeds remaining spend cap {remaining}.Raise `--cap`, lower `--count`/`--timeout`, or pass `--allow-partial` to launch what fits. | 409 | 5 | no |
| trial_credits_exhausted | Trial credits left ({remaining}) do not cover this ({needed}).Add a card to keep going (owners: `promigence billing card`, or POST /v1/billing/checkout; members ask an owner). Remaining credits are used first; running sandboxes finish. | 402 | 5 | no |
| project_cap_reached | Project {project} has {remaining} of its {limit} monthly cap left; this needs {needed}.Ask for less (`--count`, `--cap`, a shorter extension), wait for the 1st (UTC), or have an owner raise it: `promigence project cap {project} <usd>`. | 403 | 5 | no |
| spend_limit_reached | Today's spend limit {limit} has {remaining} left; this needs {needed}.Running sandboxes finish. The limit resets at 00:00 UTC and grows with each paid invoice; ask for less, or email support@promigence.ai. After a failed payment, new work waits until it is paid (owners: `promigence billing portal`). | 402 | 5 | no |
snapshot
Snapshot coverage. Something was missing that the snapshot's verify command never exercised, a signal to widen --verify.
| Code | Meaning | HTTP | Exit | Billed |
|---|---|---|---|---|
| verify_did_not_cover | `{path}` is missing but was not under a verified path; the snapshot's verify did not cover it.Billed: the environment matched what `verify` checked. Broaden `verify` (or `setup`) so the path is produced and covered, then `promigence snapshot create ...`. | 422 | 4 | yes |
grader
Grading. The grade for a run could not be computed; the run itself is kept.
| Code | Meaning | HTTP | Exit | Billed |
|---|---|---|---|---|
| grader_failed | The grade could not be computed ({kind}).Not a task failure. The time your grader ran is billed like the sandbox's own; a grade that failed on our side is not. Check that `grade.cmd` exits within `grade.timeout_ms` and that its output matches `grade.score` (exit | last_line_json | tap), then re-run. | 422 | 3 | yes |
preempt
Preemption. A sandbox on preemptible capacity was stopped because that capacity had to be given back; the lost work is credited back.
| Code | Meaning | HTTP | Exit | Billed |
|---|---|---|---|---|
| preempted | The sandbox was stopped because the capacity it was running on had to be given back.Our doing, not yours: the work lost since your last snapshot of this sandbox is credited back, up to 24 hours (a sandbox under 24 hours with no snapshot is never charged). This run asked for preemptible capacity, which is priced below on-demand for exactly this reason. Replay: `promigence run replay {run_id} --sandbox {sandbox_id}`; or re-run without `preemptible` to pay the standard rate and not be interrupted. | 502 | 3 | no |
other
Client, auth and protocol conditions.
| Code | Meaning | HTTP | Exit | Billed |
|---|---|---|---|---|
| aborted_by_client | Sandbox killed by the client.Nothing to fix; billed for the seconds used. | 200 | 130 | yes |
| expired | Sandbox timeout reached (counted from hand-off).Raise `--timeout` if the task needs longer. | 200 | 12 | no |
| not_started | Sandbox not started: outside the spend cap.Raise `--cap`; with `--allow-partial` only what fits is launched. | 200 | 12 | no |
| superseded | Attempt 1 superseded by a successful retry.Nothing to fix; attempt 1 is unbilled. | 200 | 0 | no |
| usage | {detail}.{detail}. See `promigence {command} --help`. | 400 | 2 | no |
| internal | Internal error.Not your fault. Re-run; if it repeats, tell support@promigence.ai the run or sandbox id and the time. | 500 | 1 | no |
| auth | Missing or invalid API key.Set PROMIGENCE_API_KEY or run `promigence auth login --from-env VAR`. No key yet: `promigence login` (sign in) or https://promigence.ai. | 401 | 7 | no |
| forbidden | {detail}.Do what the message names: sign in (`promigence login`), use a key from that org, or ask an owner. | 403 | 7 | no |
| not_found | {kind} `{id}` not found.Check the id; aliases resolve in your org first, then public snapshots (`promigence examples`). | 404 | 8 | no |
| unreachable | Cannot reach {url}.Retry in a moment. If the API answered this, the fault is ours: if it repeats, email support@promigence.ai with the sandbox or run id. If the API itself did not answer, check PROMIGENCE_API_URL and your connection. | 503 | 9 | no |
| rate_limited | Rate limited by {host}; retry after {retry_after_s} s.Wait `Retry-After` seconds, then retry. If the limit is an image registry's, not this API's, a `registry_secret` holding your own login for it usually raises it. Otherwise lower concurrency (see `GET /me` limits). | 429 | 10 | no |
| timeout | Wait of {wait_timeout} expired; nothing was killed.Run the id-based command: `{next}`. | 504 | 11 | no |
| not_verified | Snapshot {snapshot_id} is not verified (build {build_id}: {state}).Wait for the build: `promigence snapshot status {build_id} --wait`. | 409 | 6 | no |
| build_timeout | The build ran past its {limit_min}-minute limit.Not billed; nothing stored. Shorten `setup` (at most 64 steps, {limit_min} minutes in all) or bake the slow part into the image. | 422 | 4 | no |
| dockerfile_build_failed | Your image could not be built from the Dockerfile: {detail}.Nothing was stored and nothing was billed. Fix the Dockerfile (the lines above are the end of your own build output) and run `promigence snapshot create --dockerfile PATH ...` again. | 400 | 2 | yes |
| image_unresolvable | `{image}` could not be resolved to a digest: {detail}.Nothing was stored and nothing was billed. Check the name and tag; a private image needs `registry_secret` naming a stored secret that holds `user:password` (`promigence secrets set`). You can also pass the image already pinned as NAME@sha256:<digest>. | 422 | 2 | yes |
| enoent | `{argv0}` not found in the sandbox.Check the command name; install the program in the image or in a setup step; for a shell builtin or a pipeline, run it through a shell: `-- sh -c '...'`. | 404 | 8 | no |
| credit_unclaimed | This account has free credit waiting to be claimed.Claim it in your browser: `promigence credits claim` (owners; members ask an owner). Or add a card: `promigence billing card`. Nothing was started or charged. | 402 | 5 | no |
| card_required | A card is required for new work since {effective_at}.Add a card (owners: `promigence billing card`, or POST /v1/billing/checkout; members ask an owner). Your trial credits are kept and used first; reads and results stay available. | 402 | 5 | no |
| billing_unavailable | Card payments are not open yet.Nothing was charged. Your trial credits are kept and used first; for more credits, email support@promigence.ai. | 503 | 5 | no |
| concurrency_limit | {live} live + {requested} requested sandboxes is over your limit of {limit}.Wait for sandboxes to end or lower `--count` (`--allow-partial` covers the spend cap only); a retry of the same request fails the same way. Trial orgs get {trial_limit}; to raise it, email support@promigence.ai. | 409 | 5 | no |
| egress_cap_reached | Monthly data-out cap reached: {used_gb} of {cap_gb} GB.Use `--network none` (still allowed) or wait for the 1st (UTC). On the trial, add a card to lift the cap (owners: `promigence billing card`); with a card, choose a plan that includes more data out, or email support@promigence.ai. | 403 | 10 | no |
| tier_over_plan | The {tier} tier ({vcpus} vCPU) is over the {limit} vCPU per-sandbox limit of your {plan} plan.Use a tier of at most {limit} vCPU (`promigence snapshot create … --tier {max_tier}` is the largest; a fork runs on its snapshot's), or move to a plan that allows it: `promigence billing portal`. | 403 | 2 | no |
| network_host_refused | {host} is not on this sandbox's allowed list, so the connection was refused.Add it when you start the sandbox — `--allow {host}` — or drop `--allow` to let the sandbox reach anything. `promigence sandbox network <id>` lists everything that has been refused. | 403 | 9 | no |
| secrets_in_snapshot | Sandbox {sandbox_id} was given a secret's value, so a snapshot of it can contain that value.To take it anyway, pass `force: true` (`promigence sandbox snapshot {sandbox_id} --force`): that snapshot, and every sandbox started from it, can then contain the secret. Otherwise snapshot a sandbox that was never given a secret, and name the secret on the runs you start from it. | 409 | 2 | no |
| idempotency_key_reused | This Idempotency-Key was used for a different request.Nothing was started. Use a new key for a new request; send the original request again to get its original answer. | 409 | 2 | no |
| repo_credentials_not_supported | Credentials in repository URLs are not accepted ({field}).Nothing was built or billed. Private repositories are not supported yet: name a public repository by its plain https:// URL, with no user name, password, token, query string or fragment in it. For private code, build the image elsewhere, push it to a registry and create the snapshot from that image (`promigence snapshot create --image REF`). | 400 | 2 | yes |
| account_exists | This email address has an account that signs in with {sign_in_with}.Nothing was created. Sign in with {sign_in_with} instead: `{sign_in_command}`. If the browser signs you in with the method you just used, open the sign-in link in a private window. If you did not create that account, write to support@promigence.ai. | 409 | 7 | no |
| invite_invalid | This invite code is not valid.Nothing was created. Check the code and try `promigence signup` again, or ask the person who invited you for a new code. | 403 | 7 | no |
| invite_required | New accounts need an invite code right now.Nothing was created. Run `promigence signup` and enter your invite code when asked. If you already have an account, sign in with the method you used before (`promigence login`). | 403 | 7 | no |
| key_expired | This API key expired at {expired_at}.Create a new key (`promigence keys create`, or sign in again with `promigence login`) and replace this one wherever it is used. | 401 | 7 | no |
Every error carries a fix
The fix field is mandatory in the schema. An agent that gets an error it cannot act on retries blindly, which costs episodes and tells you nothing. Handling them in TypeScript