retry field to learn whether to send it again. Read outcome to learn whether your work started.
Read the Error Response
Every HTTP error has this shape. These values come from a request to a path that does not exist.Read the Retry Field
retry has four values. The HTTP status never decides a retry: a 409 can be safe or after_fix.
Treat a
retry value you do not know as never. Error codes lists each code’s value.
Let the Client Retry for You
The SDKs and the CLI resendretry: safe errors twice, with the same idempotency key, and never any other value. Pass max_retries or maxRetries to change the count; 0 turns it off. This not_found raises at once because its retry is not safe:
retry_after_seconds, or 0.5 seconds doubling to 8 when it is null; a longer wait is raised, not slept. The CLI always uses two. Over plain HTTP, wait and send the same request with the same Idempotency-Key.
Decide by Outcome, Not by Status
The HTTP status does not say whether the work happened. A202 is a success status, not a completion: the work is still pending. A 4xx or 5xx can carry any outcome, including unknown and completed.
Check the operation before you act on unknown or partial.
Check Whether Your Work Started
An
unknown or partial outcome always carries retry: check_outcome, whatever the code’s usual value.
An unknown 504 timeout is left for a command whose answer BoxRouter lost. Its operation reads completed with
settled_by: wait_timeout, which means BoxRouter stopped waiting, not that the command ended.
A process-start failure after dispatch is partial or unknown, and a process wait_for_port ended is partial.
Check the sandbox and any effects before starting another command. A failed process removal can
also report not_started after it began removing retained output; check what remains before retrying.
Two cases have no outcome or retry field:
- A command that exits nonzero or reaches
timeout_secondsis a result, not an error.execreturns itsexit_code,timed_out,stdoutandstderr;check=TrueraisesCommandFailedfor either. - A transport failure has no BoxRouter response. If the client never connected, its message
says the request was not sent: treat that as
not_started. If the connection dropped after sending, treat it asunknown.
Inspect an Uncertain Operation
An error withretry: check_outcome can include an operation_id. Reading it never repeats the work.
In the SDKs, operations.wait reads it until the outcome is known and raises a timeout error if it
stays unknown. The CLI and HTTP examples read it once. Inspect the resource and its effects before retrying if no ID is available. Keep the request ID
and idempotency key.
The CLI and HTTP examples need jq; the HTTP example also needs curl. For real work, inspect effects before
deleting an uncertain sandbox.
In this example the sandbox does not exist, so the call fails before anything runs and reports not_started. Its retry is
check_outcome, but there is no operation_id, so nothing is read.
unknown, look at the resource as well: the sandbox’s state, or the files the
command writes. A missing log line does not prove nothing happened. If it stays unknown, contact support with the
operation_id and the idempotency_key. Do not create a new idempotency key to get around an unresolved operation.
Recover a Lost Creation Response
Send the create request again with the same body andIdempotency-Key. You choose this key to identify
one particular call. BoxRouter looks it up in its records:
- If the first request completed, you get the same sandbox and nothing new is created.
- If it is still unresolved, you get
409with itsoperation_id, and nothing is sent to a provider. - If BoxRouter never received it, this request creates the sandbox.
- If the body differs, you get
409 conflictand nothing happens.
409 conflict and creates nothing. See the
retention policy. Search by label, or ask support if you are unsure.
Fix It by Symptom
Handle a code you do not know by its
retry value, because new codes can be added.
Keep the Diagnostic Identifiers
Every HTTP error has arequest_id. Keep the operation_id when the error includes it, and save
the idempotency key you sent with the request. Provider failures can include provider,
native_status and native_code; a failed create can list attempted routes in details.attempts
(route result).
When available, the CLI prints these on stderr. Python’s boxrouter.api_error.APIError exposes them in snake
case (operation_id), TypeScript’s APIError in camel case (operationId), and both keep the whole error
body in error. Send them when you ask for help, and keep API keys, provider credentials, and workload data
out of the report.
When Cleanup Fails
Leaving a Pythonwith block or TypeScript finally requests destruction; it does not confirm it.
If the sandbox reads destroying, BoxRouter finishes the destroy without you; to see the result,
inspect the operation before retrying.
With the ID, check its state. If it is destroying, check again. If it is running
or paused and no deletion is pending, follow the destroy instructions.
In Python, the cleanup error carries the sandbox ID. If your code also failed, a
BaseExceptionGroup holds both errors. In TypeScript, a finally error replaces the earlier one,
so catch them separately when both matter.
Pressing Ctrl-C during boxrouter run requests destruction and prints the boxrouter get command that confirms it.
Next Steps
Error Codes
Every code, whether work started, and what to do.
Check Your Limits
Which limits apply and who can change them.
Create a Sandbox
Destroy what you create so a failure leaves nothing behind.