Skip to navigation

Errors

One envelope, stable codes, and a hint telling you what to do next
View as Markdown

Every failing request returns the same JSON shape, with an HTTP status that matches. Branch on error.code — it is a stable part of the API contract. Never parse error.message, which is written for humans and may change.

{
"error": {
"code": "image_disabled",
"message": "Sandbox image 'Desktop Browser' is disabled.",
"hint": "Enable the image with PUT /sandbox-images/{id} or pick an enabled one.",
"docs_url": "https://docs.modbox.run/errors#image_disabled",
"retryable": false,
"request_id": "req_3f8a1c9e42b7",
"details": null
},
"detail": "Sandbox image 'Desktop Browser' is disabled."
}
FieldMeaning
codeStable identifier. Branch on this.
messageHuman-readable explanation. Do not parse.
hintConcrete next step that usually resolves the error.
docs_urlLink to the section for this code.
retryabletrue when retrying the identical request may succeed.
request_idCorrelation id, also in the X-Request-Id header. Quote it in support requests.
detailsField-level problems, present on validation errors.

detail is deprecated. It mirrors error.message so older clients keep working, and will be removed in a future release.

Retry behaviour

retryable tells you whether to back off and try again, or fix the request first. Retrying a non-retryable error will fail identically every time.

resp = client.post("/sandboxes/provision", json=payload)
if resp.status_code >= 400:
err = resp.json()["error"]
if err["retryable"]:
time.sleep(backoff) # safe to retry as-is
else:
raise RuntimeError(err["hint"] or err["message"])

On 429 (rate_limited), wait the number of seconds in the Retry-After header before retrying.

Validation errors

A 422 lists exactly which fields are wrong, so you can correct them without guessing:

{
"error": {
"code": "validation_error",
"message": "The request body or parameters failed validation.",
"hint": "Fix the fields listed in 'error.details' and resend the request.",
"retryable": false,
"details": [
{ "field": "body.task_id", "issue": "Field required" },
{ "field": "body.ttl_seconds", "issue": "Input should be less than or equal to 86400" }
]
}
}

Error codes

Authentication and authorization

CodeStatusWhat to do
unauthenticated401Send Authorization: Bearer <token>.
invalid_token401Token expired or revoked — refresh it or create a new one.
invalid_credentials401Wrong username or password. Do not retry unchanged.
email_not_verified403Open the verification link before logging in.
forbidden403The credential is valid but lacks access to this resource.
insufficient_role403Ask an owner or admin to raise your role.

Workspaces

CodeStatusWhat to do
workspace_required400Send the X-Workspace-Id header.
workspace_forbidden403You are not a member. List yours with GET /workspaces.
workspace_not_found404The workspace does not exist.
member_not_found404No such member in this workspace.

Sandboxes

CodeStatusWhat to do
sandbox_not_found404No sandbox with that id or name that you can access.
sandbox_name_conflict409Pick another name or destroy the existing sandbox.
sandbox_not_ready409Still provisioning — poll or call POST /sandboxes/wait. Retryable.
provisioning_failed502Check the image and quota, then retry. Retryable.

A sandbox you can’t access reports sandbox_not_found, not forbidden. Confirming that someone else’s sandbox exists would leak information, so the two cases are deliberately indistinguishable.

Images and registries

CodeStatusWhat to do
image_not_found404List images with GET /sandbox-images.
image_disabled400Enable it, or choose an enabled image.
image_immutable403Global default images are read-only — copy one instead.
registry_not_found404List credentials with GET /registries.

API tokens

CodeStatusWhat to do
api_key_not_found404List your tokens with GET /api-tokens.

Generic

CodeStatusWhat to do
validation_error422Fix the fields in error.details.
not_found404The resource does not exist.
conflict409The resource already exists or is in the wrong state.
rate_limited429Wait for Retry-After seconds. Retryable.
internal_error500Retry with backoff; contact support with request_id. Retryable.
upstream_error502An infrastructure dependency failed. Retryable.