Errors

The Shardflux error envelope and every error code and details.reason the API and the cell gateway return, with HTTP status, meaning and what to do.

The error envelope

Every non-2xx response from the application API and from the cell gateway has the same body:

JSON
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Try again later.",
    "request_id": "req-9b0e1d2c3f4a5b6c7d8e9f00",
    "retryable": true,
    "details": { "retry_after_seconds": 42 }
  }
}
Field Meaning
code A closed set of snake_case codes (below). Branch on code and details.reason, never on message.
message Safe, human-readable English. Show it when you have nothing better for a code.
request_id The same value as the X-Request-Id response header. Include it when you contact support.
retryable Whether the same request may succeed later.
operation_id Optional. The lifecycle operation that caused the error; poll it with GET /v1/operations/{id}.
details Optional. Per-code details, most often reason, plus fields such as field, names or limit.

Treat a code or reason you do not know as a generic error: show message and use retryable. New reasons can appear at any time within /v1.

A few 502, 503 or 504 responses from the network edge carry a different body. Treat them as retryable. The SDKs raise ShardfluxProtocolError for them.

Retrying

Situation Retry?
GET or HEAD, and retryable: true or status 429, 502, 503 or 504 Yes, with exponential backoff, honouring Retry-After.
A mutation sent with an Idempotency-Key, after a retryable error or a network failure Yes, with the same key and the same body.
A mutation without an Idempotency-Key Not automatically: the request may have taken effect.
retryable: false No. Change the request, the plan or the credentials first.
409 with an operation_id (operation_in_progress, workspace_not_running, workspace_busy) Wait for the operation, then retry.
Cell 409 stale_epoch Once, after getting a new tool token.
429 quota_exceeded, and cell 503 service_unavailable with details.reason: host_capacity Yes, any request, after Retry-After: it was refused before anything ran.
An execution of a file-first workspace, after a network failure or a retryable error Yes, with the same execution_id and body: the command runs at most once.
409 conflict with revision_mismatch or tree_revision_mismatch Not as it is. Read the current state, then send a new request.

The TypeScript and Python SDKs apply these rules for you. They send a request refused with 429 quota_exceeded or 503 host_capacity again whatever its method, after the full Retry-After, for up to admissionTimeoutMs / admission_timeout (2 minutes by default; TypeScript 0.22.0+, Python 0.18.0+).

Application API codes

Codes returned by https://api.shardflux.dev/v1:

Code HTTP Retryable Meaning What to do
bad_request 400 no Malformed JSON, or Content-Type: application/json with an empty body. Fix the request. Omit the header on a request without a body.
token_invalid 400 no An emailed link (email verification, password reset, email change) or a 6-digit email code is invalid, expired or already used; 5 wrong attempts end a code. Request a new link, or a new code (shard verify).
unauthenticated 401 no The Authorization header is missing or not Bearer, or the API key or a person's CLI session is malformed, unknown, revoked or expired. Check SHARDFLUX_API_KEY; create a new key in the console if it was revoked. For a session, sign in again (shard auth login).
invalid_credentials 401 no Sign-in (/v1/auth) with a wrong email or password, or a wrong two-factor or recovery code. The same answer for an account that does not exist. Check the credentials. Repeated wrong codes lock the second factor (429, mfa_locked).
entitlement_required 402 no Your plan does not allow this start, build or volume (see reasons). Change the plan or settle the payment.
card_required 402 no Compute runs on organizations with a card on file, and this one has none yet (see card_required). details has reason, organization_id, plan and add_card_url. The organization's creator saves a card at details.add_card_url (nothing is charged); then start again. Do not retry in a loop.
allowance_exhausted 402 no A compute allowance of your plan is used up and overage is off or paused, or overage reached its spend cap (see reasons). details has reason, allowance, meter, unit, used, included, resets_at, plan, exhausted and spend_cap. Act on details.reason. Do not retry in a loop.
quota_exceeded 403 no A plan limit was reached. details.limit names it, with limit_value and current (see limits). Act on details.limit, or upgrade.
quota_exceeded 429 yes An open, resume or fork would start one more workspace than the plan's workspaces working at once, and none stopped working within 10 seconds (see 429 quota_exceeded). Nothing started. Retry after Retry-After; the SDKs do (TypeScript 0.22.0+, Python 0.18.0+). Or upgrade.
forbidden 403 no The key may not do this: a tool it lacks (details.denied_tools, permitted_tools), an owner or admin action, an action only a person can take ("API keys cannot perform this action."), or a reason below. Use a key with the permission, sign in as a person (shard auth login), or ask an owner or admin.
mfa_required 403 no A person's session is still waiting for its second factor. Complete the sign-in: POST /v1/auth/mfa/challenge, or shard auth mfa --code <code>.
email_unverified 403 no A person's session before the email address is verified. A provisional account (an --email signup) gets it only on Checkout, the billing portal, invoices, spend policy, invitations and credential changes. Verify it with a 6-digit code (shard verify, or POST /v1/auth/verify-email/code) or the emailed link (shard auth verify-email '<link>').
step_up_required 403 no A sensitive action (exports, deletions, email and two-factor changes) without a password check in the last 10 minutes. Step up (POST /v1/auth/step-up, or shard auth step-up), then retry.
not_found 404 no The resource does not exist, or it is outside the key's project. The two are not distinguished. An open with a template that does not resolve names it in details.template. Check the id, key or slug; GET /v1/templates (shard templates list) lists the templates you can open.
conflict 409 depends The resource is in a state that refuses the request; details.reason says which (see reasons). Act on the reason.
payload_too_large 413 no The request body is too large. Send less.
unsupported_media_type 415 no The body is not JSON. Send Content-Type: application/json.
validation_failed 422 no The request is invalid: details.issues (schema checks: path and message) or details.field and details.reason (see reasons). Fix the named field.
idempotency_mismatch 422 no The Idempotency-Key was already used with a different request within 24 hours. Use a new key for a new request.
rate_limited 429 yes Too many requests. Retry-After gives the seconds to wait, also in details.retry_after_seconds. Wait, then retry.
internal_error 500 depends An unexpected server error. Follow retryable: retry safe requests only when it is true; otherwise report the request_id.
dependency_unavailable 503 yes A dependency is temporarily unavailable (a reason may say which). Database connection and query timeouts include Retry-After: 2. Retry idempotent calls with backoff, honouring Retry-After.
capacity_pending 503 yes An operation state (see Operation errors): an open, resume or fork reports it on its operation. A resize returns it as an HTTP error when the workspace's host cannot take the larger disk right now (details.reason: host_disk_insufficient or limit_exceeded, with the operation_id). Keep waiting for the operation. Send a resize again later.
stale_epoch 409 yes Returned by the cell gateway (below). Get a new tool token.
authorization_pending 400 yes Browser sign-in (POST /v1/auth/device/token): the person has not approved yet. details.interval gives the seconds between polls. Poll again after details.interval seconds.
slow_down 400 yes Browser sign-in: polled faster than the interval. details.interval (also Retry-After) is the new interval, 5 seconds longer. Poll at the new interval.
access_denied 400 no Browser sign-in: the person denied it on the sign-in page. Start a new sign-in (shard auth login --browser).
expired_token 400 no Browser sign-in: the device code is unknown, already used, or was not approved within its 10 minutes. Start a new sign-in.

Requests with an API key never receive token_invalid, invalid_credentials, mfa_required, email_unverified, step_up_required or the browser sign-in codes: they come from /v1/auth and from requests with a person's CLI session (Authorization: Bearer sfu_...; see Authentication). The console's own routes also use csrf_failed, which /v1 never returns.

Cell gateway codes

Codes returned by a workspace's cell_endpoint (exec, files, terminals, processes, git, browser):

Code HTTP Retryable Meaning What to do
bad_request 400 no Invalid query or path parameters, or malformed JSON or unknown fields. Fix the request.
unauthenticated 401 no No tool token, or it is malformed, expired or revoked (details.reason: revoked_watermark, revoked_workspace, revoked_api_key, revoked_api_key_tools). Get a new tool token and retry once.
forbidden 403 no The token is for another workspace or lacks the tool (details.tool); or a named secret is not available (secret_not_available). Request a token with the tool; check the key's permissions.
not_found 404 no The workspace is unknown or deleted, or a file does not exist (on a file-first workspace also any path outside /home/user, outside_tree_root). Stop, or fix the path.
conflict 409 depends stale_endpoint (the workspace moved to another cell), a session_id or Idempotency-Key reused for a different request (operation_id_reused), an interrupted attempt whose outcome is unknown (operation_interrupted), legacy_disk_layout, guest_feature_unavailable, and the reasons of file tools and hosts and file-first workspaces. stale_endpoint: read the workspace again and use its new cell_endpoint. Interrupted: reattach to the session instead of re-running.
stale_epoch 409 yes The token was issued before the workspace's latest suspend, resume or move. Get a new tool token and reconnect with your last offsets.

Computer use (/computer, /computer/actions, /computer/stream):

Code Status Retryable Meaning What to do
forbidden 403 no The token lacks the computer tool (details.tool): computer use is off, or the token was issued before it was switched on. Switch it on and get a new tool token (the SDKs retry once with a fresh token).
conflict 409 no computer_use_unavailable: the template version has no desktop. desktop_failed: the desktop could not start (the template's session script). Use a template with a desktop; check /etc/shardflux/desktop/session.
validation_failed 422 no invalid_computer_action: an action is invalid (details.index): an unknown action, a missing or extra parameter, a coordinate outside the screen, or a batch limit. Fix the action at details.index.
dependency_unavailable 503 yes desktop_failed: the desktop did not answer. Retry.
workspace_not_running 409 yes The workspace is suspended or not on a host. The call was not executed. A suspended workspace's file reads are answered from its disk while a host still holds it; offline_unavailable and offline_budget say that such a read could not be. Resume it (or open the key), then retry with a new token.
workspace_busy 409 yes A lifecycle operation holds the workspace (operation_id; details.operation_kind; details.tool_gate: read_only or closed; or details.reason: workspace_fenced), or an execution of a file-first workspace runs (execution_in_progress, details.execution_id). The call was not executed. Wait for the operation or the execution, then retry. During read_only, reads still work.
validation_failed 422 no Invalid parameters (paths, a relative cwd (invalid_cwd), modes, signals, terminal size, a reserved sfstart. session id, env_collision, a resource_hint other than auto, light or heavy), or a patch that does not apply (edit_not_found, edit_ambiguous, edit_not_text, patch_invalid). Fix the request.
payload_too_large 413 no The body, stdin (over 1 MiB) or patch (over 7 MiB) is too large (details.max_bytes). Send less.
range_not_satisfiable 416 no The Range is not a single bytes=start-end, or starts past the end of the file (details.size). Request within the size.
quota_exceeded 429 yes The call would wake this workspace while your organization already has its plan's workspaces working at once, and none stopped working within 5 seconds (see 429 quota_exceeded). Nothing ran. Retry after Retry-After; the SDKs do (TypeScript 0.14.0+, Python 0.10.0+), for up to 2 minutes from TypeScript 0.22.0 and Python 0.18.0. Or upgrade.
workspace_disk_full 507 no The workspace filesystem has no available space or inodes, or a filesystem quota was reached. details.used_bytes and limit_bytes report measured filesystem usage when available; available_bytes, used_inodes and limit_inodes identify the exhausted resource. Delete files through the files API (files.remove / files_remove) or a cleanup exec, then retry the write. Waiting alone does not free space.
rate_limited 429 yes A transient workspace concurrency limit was reached, or there were too many secret resolutions. Retry-After gives the seconds to wait. Back off and retry idempotent calls.
dependency_unavailable 503 yes The workspace host or a gateway dependency is temporarily unavailable, or the disk changed during a read of a sleeping workspace (offline_changed). Retry with backoff.
service_unavailable 503 yes The gateway cannot take the request right now. Also: the workspace's host is making room to wake it and the call waited for it as long as it could (host_capacity, see file tools, parking and hosts), a wake failed (wake_failed), or the execution cannot be placed right now (no_execution_host). Nothing was executed. Retry after Retry-After, or a second. The SDKs retry host_capacity for any call (TypeScript 0.22.0+, Python 0.18.0+).
timeout 504 yes The workspace did not answer in time. Retry idempotent calls. For exec, reattach to the session instead of starting it again.
internal_error 500 no An unexpected gateway error. Report the request_id.

The cell enum also contains capacity_pending; the gateway never returns it.

Reasons

details.reason refines a code. The tables list the reasons a request with an API key can meet; the last one adds those of a person's session.

402 entitlement_required

Reason Meaning What to do
stripe_trial_required A new account has not completed the $0 Stripe card check for its 7-day signup trial. Run shard billing trial --wait, complete the card check in Usage & billing, or choose a paid plan.
trial_identity_required Starting the signup trial also needs a verified work domain (at least 30 days old with active MX records) or a linked GitHub account (at least 30 days old). Link an established GitHub account or verify a work domain in the console, then run shard billing trial --wait, or choose a paid plan ($9/mo Developer and up).
subscription_required The organization's trial or subscription has ended, or no paid plan is active for workspace compute. Retained storage up to 10 GiB is kept. Choose a paid plan ($9/mo Developer and up) in Usage & billing or with shard billing upgrade <plan>.
free_organization_limit This account already used its signup trial in another organization; additional organizations need a paid plan for compute. Use that organization during its trial, or choose a paid plan for this one.
no_plan The organization has no active plan. Choose a plan in the console.
limits_missing The plan lacks a limit the request needs (details.missing). Upgrade.
not_included The plan does not include this feature (shared volumes; for the GPU see GPU frame requests). Upgrade.
payment_past_due A payment is overdue and the grace period ended (details.grace_until). New starts are refused; running workspaces are not affected. An owner or billing member updates the payment method.
unpaid The subscription is unpaid. As above.
operator_hold New starts are on hold for the organization. Contact support.

402 card_required

Opening, resuming or forking a workspace, or building a template, in an organization without a card on file. details.add_card_url is the console's Usage & billing page for that organization, where its creator saves a card on a Stripe page in setup mode (nothing is charged). From a person's session the same check is POST /v1/organizations/{id}/billing/card-checks: it returns the Stripe page (url), and GET .../card-checks/{card_check_id} reports succeeded once the card is saved. GET .../billing/subscription has card_required: true until then. Running workspaces keep running. A paid plan, or a plan arranged with Shardflux, counts as a card on file.

JSON
{"error":{"code":"card_required","message":"Compute runs on organizations with a card on file. The organization's creator saves one at https://app.shardflux.dev/billing?organization_id=01a1…&add_card=1; nothing is charged.","request_id":"req_…","retryable":false,"details":{"reason":"card_required","organization_id":"01a1…","plan":"free","add_card_url":"https://app.shardflux.dev/billing?organization_id=01a1…&add_card=1"}}}

402 allowance_exhausted

New opens, resumes and forks are refused, and the organization's running workspaces are suspended with their files, memory and processes kept. Nothing is deleted. See Overage. GPU-hours never refuse a start: used up, they stop only the GPU (GPU frame requests).

Reason Meaning What to do
allowance_used The plan's CPU-hours or RAM GiB-hours allowance is used up, and overage is off or not available on the plan. Upgrade, or wait for resets_at on a recurring plan. An owner or billing member can turn on overage when spend_cap is available.
overage_paused The allowance is used up, and overage is on but paused while a plan payment is past due. An owner or billing member pays the invoice or updates the payment method.
spend_cap_reached Overage charges reached the spend cap for this billing period. Wait for resets_at, have an owner or billing member raise the cap (up to the plan price), or upgrade.
details field Meaning
reason One of the reasons above.
allowance, meter, unit The first used-up allowance (cpu_hours or ram_gib_hours), its meter (cpu_seconds or memory_gib_seconds), and the unit of used and included.
used, included This period's usage and the plan's allowance, in unit.
used_meter_units, included_meter_units The same in meter units (CPU-seconds or GiB-seconds).
exhausted Every used-up allowance, each with allowance, meter, used, included and unit.
resets_at The end of the billing period. Recurring allowances and spend caps reset then; for a time-limited plan grant, this is the grant end, not a renewal.
plan The plan key, for example developer.
spend_cap null when overage is not available on the plan. Otherwise cap_minor (the configured cap, null if never set), effective_cap_minor (the cap that applies: cap_minor limited to the plan price, or the plan price when no cap is set), charges_minor (overage charged this period) and currency. Amounts are in minor units (cents).

A time-limited plan grant counts usage across its whole grant window. Exhausting its CPU-hours or RAM GiB-hours ends the grant; waiting does not renew it, and workspace compute pauses until you choose a paid plan while up to 10 GiB of retained workspace storage is kept. When spend_cap is null, overage is unavailable.

GPU frame requests

A GPU app's control socket (/run/sfgpu/<pid>.sock) answers each frame request with one JSON line. When the platform does not render it, ok is false, status is 402 and retryable is false; the workspace and the app keep running, and rendering comes back on its own once the cause clears.

JSON
{"ok":false,"error":"allowance_exhausted","status":402,"retryable":false,"reason":"allowance_used","message":"..."}
error reason Meaning What to do
entitlement_required not_included The plan has no GPU: "GPU needs the Startup or Scale plan." plan names the current plan and required_plans is ["startup","scale"]. Upgrade to Startup or Scale.
allowance_exhausted allowance_used This period's GPU-hours are used up and overage is off. Wait for the next period, upgrade, or have an owner or billing member turn on overage in the console.
allowance_exhausted overage_paused The GPU-hours are used up, and overage is on but paused while a plan payment is past due. An owner or billing member pays the invoice or updates the payment method.
allowance_exhausted spend_cap_reached Overage charges reached the spend cap for this billing period. Wait for the next period, have an owner or billing member raise the cap (up to the plan price), or upgrade.

The organization's usage summary shows the same state: gpu.cap_state is gpu_blocked and gpu.blocked_reason is the reason.

GPU video sessions

Video sessions are counted per workspace (Limits). A create past the workspace's count, or one that finds no session free on its GPU within the admission wait, is refused, and nothing is created:

API The create What it returns What to do
Vulkan Video vkCreateVideoSessionKHR VK_ERROR_OUT_OF_DEVICE_MEMORY after a wait of up to 2 s; the driver log says how long to wait before trying again Destroy a session, try again after that wait, or decode and encode in software. FFmpeg does that on its own.
NVDEC cuvidCreateDecoder, cuvidCreateVideoParser CUDA_ERROR_OUT_OF_MEMORY after libnvcuvid.so.1 has retried it for 10 s Destroy a decoder or parser, or try again.

GPU readbacks

A GPU app that waits for GPU results (Blender with SFGPU_READBACK=1, compute results read from mapped memory) or for the GPU itself (a CUDA call) takes its turn when the GPU is busy. A Vulkan readback that gets no GPU within 10 minutes ends with VK_ERROR_DEVICE_LOST, and the app's log has a [sfgpu] readback: line with the reason. Start the render again; the next one waits its turn the same way.

403 quota_exceeded

details.limit Meaning
retained_state The organization's Retained state has reached the plan's allowance (limit_value and current in GiB), so opening a new key and forking are refused. Existing workspaces keep running, waking, suspending and resuming. Delete workspaces you no longer need, or upgrade.
concurrent_template_builds Unfinished template builds. Wait for one to finish.
shared_volumes_max Shared volumes of the organization.
shared_volume_gib_max The size of one shared volume.
volume_attachments_per_workspace Volumes attached to one workspace.

429 rate_limited

Platform request and workspace-credential budgets return rate_limited, retryable: true, and a Retry-After header with the delay in seconds, also in details.retry_after_seconds. Honor that delay before retrying.

details.reason Meaning What to do
api_request_budget The API request budget is busy. Reduce polling, reuse results, and retry after the indicated delay.
tool_token_budget The workspace credential budget is busy. Reuse unexpired workspace credentials and retry after the indicated delay.

details.rate_limit identifies resource, scope (api_key, organization, or workspace), limit, window_seconds, burst, plan_key, catalog_version, and workspace_units. Keys in the same organization share its organization budget; rotating keys does not increase it. These budgets are separate from plan usage allowances and concurrent workspace admission.

429 quota_exceeded

Returned when a request would make one more workspace work than the organization's plan allows at once:

  • by the cell gateway, for a call that would wake a parked workspace. The call waits up to 5 seconds for a workspace to stop working first; calls to a workspace that is already working never wait.
  • by the API, for an open, resume or fork (details.limit: concurrent_workspaces). The request waits up to 10 seconds for a workspace to stop working first; reopening a workspace that is already running never waits.
JSON
{
  "error": {
    "code": "quota_exceeded",
    "message": "Your plan allows 10 workspaces working at once and none became free within 5 seconds; retry shortly, or upgrade.",
    "request_id": "req-9b0e1d2c3f4a5b6c7d8e9f00",
    "retryable": true,
    "details": { "limit": "concurrent_workspaces", "limit_value": 10, "current": 10, "retry_after_seconds": 2 }
  }
}
  • The request was refused before anything ran or started, so any request is safe to retry after Retry-After (also in details.retry_after_seconds): opens, commands, stdin, terminals and file writes included. The SDKs retry it: the cell gateway's since TypeScript 0.14.0 and Python 0.10.0, the API's from TypeScript 0.22.0 and Python 0.18.0. From those versions on, both are retried for up to admissionTimeoutMs / admission_timeout (2 minutes by default).
  • The API's refusal also carries details.plan.
  • A tool call, a keepalive and a wake hint can get it; a wake hint gets it at once, without the wait.
  • A WebSocket attach (exec output, terminal) is refused with the same 429 response. From a browser, the connection opens, sends this body as its first text message and closes with code 4429, whose close reason is compact JSON with code and retryable. The SDKs read both and raise the error with its own code and details (TypeScript 0.22.0+, Python 0.18.0+).

403 forbidden

Reason Meaning
template_dev_mode_role Drafts and test instances (their creation, lifecycle calls and tool tokens) need an owner, an admin or an API key with a tool permission.
secret_not_available An exec or terminal start names a secret (details.names) this workspace may not use; nothing was started. Also returned for starts while a bound secret's status is not_allowed.
edge_refused The edge firewall refused the request before it reached the API; request_id is edge. File paths on the file routes are data and pass. Send file contents and command text in the request body.

404 not_found

Reason Meaning
draft_not_found The template has no draft.
version_not_found No such template version.
path_not_found The path is not in the version's file tree (details.path).
checkpoint_not_found No such checkpoint of the workspace.
draft_state_not_found No such draft state.
package_not_found No package with that name in the ecosystem.
log_not_available The build has no full log yet; use the log tail on the build.
log_expired The build's full log is past its retention.

409 conflict

Reason Retryable Meaning What to do
operation_in_progress yes Another lifecycle operation runs on the workspace (active_operation_id, active_operation_kind). Wait for it, then retry.
email_registered no POST /v1/auth/signup with an email that already has an account. Sign in (shard auth login).
workspace_not_running yes A tool token was requested while the workspace is suspending, or while a resume that was asked for has not started yet; or a browser stream while it is not running (observed_state, desired_state). A suspended workspace does get a tool token: the cell answers it only the reads of the workspace's disk. Open or resume the workspace, then request the token.
workspace_deleted no The workspace is deleted; the key can be reused after deletion finishes. Use a new key.
resize_not_available no PATCH /v1/workspaces/{id}/caps is not available to this organization. Nothing changed. Set the caps when you open the workspace.
not_running no Suspend, or suspend when idle, of a workspace that is not running. Refresh the workspace.
already_running, not_suspended no Resume of a workspace that is not suspended. Nothing to do.
not_snapshottable, not_forkable, not_resettable no The workspace is not running or suspended. Refresh the workspace.
key_in_use no The fork's target key is taken. Choose another key.
lifetime_mismatch no The key is open with another lifetime (lifetime, requested_lifetime). Omit lifetime, or use another key.
session_lifetime no A session workspace cannot be suspended, now or when idle, and has no idle policy. Close it, or fork it to keep its state.
not_session no Close of a persistent workspace. Suspend or delete it instead.
legacy_disk_layout no Reset, save as template and changes need a layered workspace. Use a layered workspace.
layout_unsupported no The template version needs a disk layout that is not available here, or it cannot run a file-first workspace (disk_layouts). Use another template version.
computer_use_disabled no A tool token asked for the computer tool while the workspace's computer use is off. Switch computer use on for the workspace or its template, or leave computer out.
computer_use_unavailable no Switching computer use on for a workspace whose template version has no desktop. Use python-node-browser 15 or later, or a template built on it.
not_supported_for_mode, mode_mismatch no A call that the workspace's mode does not have, or a reopen with the other mode (see file-first workspaces). Use what the mode offers, or omit mode.
template_not_layered no A draft's base is not layered-capable. Choose another base.
suspend_capture_started no A suspend cancel after the suspend's capture has started (details.progress). The suspend completes. Wait for the operation, then resume the workspace.
suspend_cancel_unavailable no A suspend cancel while the suspend is not in a phase that can be canceled (details.progress). Cancel while progress.cancelable is true, or wait for the operation.
suspend_not_active no A suspend cancel after the suspend has ended (details.progress). Read the operation.
not_suspend_operation no Suspend progress or cancel of an operation that is not a suspend. Use the suspend's operation id.
draft_exists no The template already has a draft (workspace_id). Use that draft.
draft_stale no The template got a newer version since the draft was opened (latest_version, draft_base_version). Start a new draft from the latest version.
build_in_progress yes Another build of the template is unfinished (build_id). Wait for it, then publish.
build_finished no Cancel of a build that already finished. Nothing to do.
file_list_unavailable no The version has no file list (published before file lists existed). Open a draft or test instance to see its files.
file_list_indexing yes The file list is still being indexed. Retry shortly.
guest_feature_unavailable no The workspace's template cannot do this (for example list changes). Use a newer template version.
package_index_unavailable pip: yes; apt: no The base has no apt index, or the pip name index is not built yet. Type the package name; the build checks it.
template_archived, version_archived no The template or version is archived. Use another template or version.
name_taken no A secret with that name exists in the scope. Choose another name, or rotate the existing secret.
secret_deleted no The secret is deleted. Create it again.
concurrent_rotation yes Another rotation of the secret ran at the same time. Retry.
version_mismatch no Egress policy or spend policy update with a stale If-Match (current_version). Read the policy again and re-apply.
name_in_use no A live volume of the project has that name. Choose another name.
volume_attached no Delete of a volume that is still attached (attachments). Detach it, or delete with force.
volume_creating, volume_not_available yes while creating The volume is not available yet (volume_state). Wait until it is available.
volume_not_shared no The volume belongs to another project. Use a volume of this project.
volume_name_ambiguous no A volume name in an open's volumes matches more than one shared volume (name). Select an unambiguous project volume name.
already_attached, detach_in_progress, attach_in_progress attach_in_progress: yes The volume is already attached with another path or mode, or an attach or detach is still running. Detach first, or retry shortly.
mount_path_conflict no The mount path equals or nests with another mount of the workspace. Choose another path.

Two retryable 409s have no reason: a request whose Idempotency-Key is still in progress, and an open of a key that another open is creating at the same moment. Retry the same request (with the same key).

422 validation_failed

Workspaces, inputs and secrets:

Reason Meaning
reserved_key_prefix Workspace keys starting with sf: are reserved.
like_outside_project like does not name a live workspace in the caller's project (field: like).
like_not_standard like names a workspace that is not a standard workspace (field: like).
mount_path_conflict Two of an open's volumes have equal or nested mount paths (field: volumes). Choose distinct, non-nested absolute mount paths.
mode_not_available This deployment does not offer file-first workspaces (field: mode).
not_supported_for_mode lifetime: "session" for a file-first workspace, which is always persistent (field: lifetime), or allocation_mode: "elastic" for one (field: caps.allocation_mode); file-first executions have a fixed size.
requires_elastic memory_mib_held for a file-first workspace, whose executions have a fixed size (field: caps.memory_mib_held).
exceeds_memory_mib memory_mib_held above memory_mib (field: caps.memory_mib_held, or memory_mib_held on a resize).
shrink_not_supported A resize to a smaller disk_gib (field: disk_gib, details.current_disk_gib); disks grow only. Nothing changed.
confirm_destructive_required Reset needs {"confirm_destructive": true}.
secret_not_available A secret name to bind is unknown or not usable by this workspace (details.names); nothing changed.
env_collision A bound secret has the name of a template environment variable or text input (details.name).
input_required A required input is missing (details.names, details.kind).
input_unknown The template declares no input with that name (details.names).
input_invalid An input value is not a string, is a secret input, is over 4096 bytes, or contains CR, LF or NUL.
egress_widening A workspace egress policy allows more than the template's egress ceiling (outside).
reserved_prefix, nul_character, too_large Secret names starting with SHARDFLUX_, a value with a NUL byte, or a value over the size limit.

On a resize with at_least: true, a smaller disk_gib leaves the disk unchanged and allows the other named resources to grow. A resize without that flag keeps the shrink_not_supported response for a smaller disk.

Workspace tools (the cell gateway):

Reason Meaning
invalid_cwd An exec, execution or terminal start named a cwd that is not an absolute path (or has a NUL byte, or is over 4096 bytes); details.field is cwd. Relative paths are not resolved: the message names the absolute path the cwd likely means, e.g. use "/home/user/app". Nothing ran.

Templates and builds:

Reason Meaning
invalid_recipe The recipe does not match the recipe v2 schema (details.field).
invalid_path, invalid_package, invalid_host A path, package name or host in the recipe is invalid (details.field).
invalid_path at recipe.immutable or recipe.immutable[<i>] An immutable path is not an absolute, clean path of at most 256 bytes; is /, under /proc, /sys, /dev, /run or /tmp, or a parent of a path Shardflux owns (such as /etc, /usr or /var/lib); repeats or contains another; or the list has more than 8 entries (limit). Use directories of your own, such as /opt/acme.
immutable_path_removed The new version leaves out an immutable path of the template's current version (details.removed, details.open_version). Versions keep every immutable path: add the paths back.
immutable_paths_unsupported_base The build cannot carry the template's immutable paths (details.base, details.paths). Build from a template.yaml on python-node-browser 11 or later or ubuntu-24.04 2 or later; to save a workspace as a version, use a workspace created on such a base.
invalid_settings settings do not match the settings schema, or settings.defaults was given together with defaults.
base_not_found, base_archived, base_not_published, architecture_not_supported The recipe's base cannot be used.
base_not_layered A recipe v2 build needs a layered-capable base.
base_required A draft of a template without a published version needs base.
language_unavailable The base does not offer that language or version (available).
language_conflict The base already has another version of the language.
too_many_files, too_many_steps, too_many_hosts, recipe_too_large The recipe is over a size limit (limit).
platform_owned_path A file destination is owned by the platform.
upload_required A file entry still names a local from path: upload it and send upload: "sha256:<hex>".
upload_missing The organization has no upload with that SHA-256.
upload_digest_mismatch The same SHA-256 was announced with another size, or the bytes do not match.
upload_too_large An upload is over 5 GiB.
extra_hosts_without_auto, allow_hosts_without_allowlist, allowlist_empty The build network settings are inconsistent.
services_unsupported The base's guest agent cannot run services; remove settings.services or use a newer base.
invalid_defaults An idle timeout without lifetime: "session".
too_many_acknowledged_findings More than 200 acknowledged scan findings.
platform_template_slug, reserved_template_slug, slug_not_supported_by_builder The slug cannot be used for an organization template.

Egress policy validation returns every problem in details.issues, each with path, reason and message.

Spend policy (overage and its spend cap, changed by an owner or billing member; details.field names the field):

Reason Meaning
overage_unavailable The plan has no overage (a trial, or no active subscription).
spend_cap_required Turning overage on needs a spend cap.
spend_cap_below_minimum The cap is below $1 (min_minor).
spend_cap_above_plan_price The cap is above the plan price (max_minor).
spend_cap_below_charges Overage already charged more than that this period (charges_minor). Turning overage off is always allowed.

507 workspace_disk_full

For a reachable workspace with an exhausted filesystem, the files API can read and remove files without starting an exec session. Cleanup exec sessions use bounded memory-backed control storage and output; their session labels include shardflux.session_storage: memory_until_cold_boot. They survive an agent restart or memory resume; a cold boot ends the processes and clears those emergency session records. Ordinary exec sessions keep their durable storage. Freeing filesystem space restores ordinary writes and exec storage. If the workspace cannot resume, contact support with the request id; credential delivery still requires writable space.

503 dependency_unavailable

details.reason: budget_backend_busy or budget_policy_unavailable means request-budget accounting is temporarily unavailable. The error is retryable, with a one-second Retry-After and details.retry_after_seconds. Back off and retry; this does not mean the plan allowance is exhausted.

Reason Meaning
package_search_unavailable, package_index_loading Package search is temporarily unavailable. Retry.
uploads_not_configured Template uploads are not available in this environment.
build_log_bucket_not_configured, signing_failed The full build log cannot be served right now; use the log tail on the build.

File tools, parking and hosts

Reasons of the cell gateway for search, patches and reads of sleeping workspaces, for parked workspaces and for writes under immutable paths:

Code and reason Retryable Meaning What to do
409 conflict revision_mismatch no A patch's expected_revision is not the file's current revision (details.current_revision, absent for a missing file). Nothing changed. Read the file again and redo the edit.
422 validation_failed edit_not_found no An edit's old text does not occur in the file (details.index, counting from 0). Nothing changed. Fix that edit.
422 validation_failed edit_ambiguous no An edit's old text occurs more than once (details.index). Nothing changed. Include more surrounding text, or set replace_all.
422 validation_failed edit_not_text no The file is not UTF-8 text, so edits cannot apply. Replace it with content.
422 validation_failed patch_invalid no The patch does not have exactly one of edits and content. Fix the request.
409 workspace_not_running offline_unavailable, offline_budget yes A read of a suspended workspace could not be answered from its disk. Resume the workspace and read again. The SDKs, the CLI and the MCP server do.
503 dependency_unavailable offline_changed yes The disk changed during a read of a sleeping workspace, because it started running. Retry: the running workspace answers.
503 service_unavailable host_capacity yes The workspace's host is making room to wake it. The gateway held the call while it did, for up to 25 seconds for a command, terminal or git start, 30 for an output or terminal attach, and 50 for other calls; details.waited_seconds says how long, and details.queue_position, when present, is the call's place in line. The call was not executed. Retry after Retry-After; the SDKs do for any call (TypeScript 0.22.0+, Python 0.18.0+).
503 service_unavailable wake_failed yes Waking the parked workspace failed; its state is intact. The call was not executed. Retry.
409 workspace_busy workspace_fenced yes A lifecycle operation is taking over the workspace. The call was not executed. Wait for the operation, then retry.
409 conflict read_only_path no A write, patch, mkdir, move or delete under one of the template's immutable paths. Nothing changed. Write to the workspace's own files; to change a tool, copy it there first.
409 conflict host_feature_unavailable no This call is not available for this workspace (details.feature: file_search or file_patch); its reads and stats come without revisions. Use another way: grep in a command instead of a search, a read and a write instead of a patch.

Inbound ports

Reasons of the ports routes of the API:

Code and reason Retryable Meaning What to do
403 forbidden inbound_ports_not_available no Inbound ports are not enabled for the organization. Contact support to enable them.
404 not_found port_not_exposed no A token, link or callback URL of a port that is not exposed (details.port). Expose the port first.
409 conflict port_limit no The workspace already exposes the most ports (details.limit). Close a port first.
422 validation_failed port_reserved no Port 61001 is the workspace desktop's control port; it cannot be exposed. Watch the desktop with computer.stream(), which exposes its viewer ports.
409 conflict not_supported_for_mode no A file-first workspace has no processes to serve a port. Use a processful workspace.

Answers of a port URL itself carry the header x-shardflux-reason; a response without it came from your app. The body is the usual error envelope (an HTML page when a browser asks for one):

Status and reason Retryable Meaning What to do
401 port_auth_required no The request carries no port token, link cookie or callback path. Send Authorization: Bearer sfp_... or X-Shardflux-Token, or open a signed link.
401 port_token_invalid no The token is malformed, expired, or of the wrong kind for where it was sent (a link token in a header). Mint a new token or link.
401 port_token_revoked no The port was closed (and maybe exposed again) since the token was minted, or its key or user was revoked. Mint a new token or link.
401 callback_invalid no The callback URL was replaced or revoked. Register the current callback URL.
403 port_token_scope no The token is for another workspace or port. Use the token of this port.
403 inbound_ports_not_enabled no Inbound ports are not enabled for the organization. Contact support.
404 port_not_exposed no The port is not exposed. Expose it.
404 unknown_host, reserved_path no No workspace is served at this address, or the path is under /__shardflux/. Check the URL.
413 body_too_large no The request body is larger than 1 GiB. Send less.
429 rate_limited, connection_limit yes Too many requests or open connections (Retry-After). Back off and retry.
502 port_not_listening yes The workspace runs, but nothing accepted connections on the port in time. Start the server on 0.0.0.0:<port>, then retry.
502 upstream_error yes The app closed the connection without a complete answer. Check the app's logs; retry.
503 wake_timeout yes The workspace did not run within 30 seconds (Retry-After). Retry.
409 wake_failed no The workspace's latest start failed and needs recovery. No new start was requested. Contact support to recover the workspace, then retry.
503 wake_failed, host_capacity yes A transient wake failed (wake_failed), or the workspace's host was still making room to wake it after 30 seconds (host_capacity). Retry-After says when to retry. Retry.
402 entitlement_required, card_required, allowance_exhausted; 403 quota_exceeded no Starting the workspace was refused as a resume would be. See the reason above.

File-first workspaces

Reasons of the API and the cell gateway for file-first workspaces:

Code and reason From Retryable Meaning What to do
409 conflict not_supported_for_mode API, cell no The call does not exist for the workspace's mode (details.mode, details.operation): suspend, resume, snapshot, fork, reset, save as template, volumes, idle policies, exec sessions, terminals, processes, git, browser, changes or keepalives of a file-first workspace, or reading an execution of a processful one. Use what the mode offers.
422 validation_failed not_supported_for_mode API, cell no lifetime: "session" when opening a file-first workspace, or execution_id / output_limit_bytes in an exec of a processful workspace. Leave the field out.
409 conflict mode_mismatch API no The key belongs to a workspace of the other mode (mode, requested_mode). A mode never changes. Omit mode, or use another key.
422 validation_failed mode_not_available API no This deployment does not offer file-first workspaces. Use a processful workspace.
409 conflict layout_unsupported API no The template version has no layered disks, which executions need (disk_layouts). Use a layered template version.
409 conflict tree_revision_mismatch cell no If-Match names another revision than the tree's (details.current_tree_revision). Nothing changed. Read what changed, then retry with the new revision.
404 not_found, 422 validation_failed outside_tree_root cell no A read (404) or a change (422) of a path outside /home/user (details.tree_root). Use a path under /home/user.
409 workspace_busy execution_in_progress cell yes An execution runs (details.execution_id); a second execution or a file change must wait. Retry when it has ended. The SDKs and the CLI wait.
409 conflict execution_id_reused cell no The execution id was already used for a different request. Use a new id for a new command.
503 service_unavailable no_execution_host cell yes The execution cannot be placed right now (Retry-After). Nothing ran, and the id stays unused. Retry with the same id. The SDKs and the CLI do.

An execution that ended failed or lost published nothing. Its result carries error with code, message, retryable (whether a new execution may succeed) and details.reason, for example exec_failed_to_start or layout_unsupported.

With a person's session

Account actions on /v1 need a person's CLI session (sfu_..., from /v1/auth or shard auth login); an API key gets 403 forbidden for them. These are the reasons they, and signing up, add to the tables above:

Code and reason Meaning What to do
409 conflict subscription_exists Checkout for an organization that already has a subscription. Change plans in the billing portal (POST .../billing/portal-sessions, shard billing portal).
409 conflict not_creator, trial_active, trial_identity_required, trial_used, trial_unavailable A card check (POST .../billing/card-checks) by someone other than the organization's creator, or for an organization that already has a card on file and is on its trial, awaits GitHub or work-domain verification (trial_identity_required), has used its creator's trial (one signup trial per person, per card and per GitHub account across all accounts, ever), or has no trial to start (details.trial). The creator verifies GitHub or a work domain, or keeps going on the trial, or chooses a paid plan (GET /v1/billing/catalog).
409 conflict checkout_superseded A concurrent checkout request for another plan replaced this one. Retry.
409 conflict subscription_active, checkout_pending Organization deletion while a subscription is active (billing_state, current_period_end) or a Checkout session is open (checkout_id, expires_at). Cancel the subscription in the billing portal, or let the checkout finish or expire.
409 conflict organization_ownership Account deletion while you are the only owner of an organization with other members, or its only member (details.organizations). Transfer ownership of those organizations, or delete them.
409 conflict last_owner A role change or removal would leave the organization without an owner. Make someone else an owner first.
409 conflict concurrent_invitation An invitation to the same address is being created at the same moment. Retry.
409 conflict export_expired The data export's download is past its expiry (expires_at). Create a new export.
409 conflict export_too_large An audit export would exceed its row limit (max_rows, matched_rows_at_least). Narrow the time window or the filters.
422 validation_failed email_disposable Signing up (shard auth register), or changing the sign-in email, with an address at a disposable-email domain or a subdomain of one (field: email). Use a permanent email address.
422 validation_failed plan_not_purchasable Checkout for a plan that cannot be bought, such as the signup trial (field: plan_key). Choose a paid plan from GET /v1/billing/catalog.
429 rate_limited mfa_locked Too many wrong two-factor codes (retry_after_seconds). Wait, or reset the password by email.

Operation errors

A failed operation (state: failed or canceled) carries error: {code, message, retryable, details}. The SDKs raise it as OperationFailedError (errorCode, retryable).

error.code Retryable Meaning What to do
capacity_unavailable yes The start passed its deadline: 15 minutes after its creation, later while new capacity is starting for it or its files are being moved to new storage (at most an hour; details.reason: preserving_files for the second, with the files kept), or after 2 minutes when no more capacity can be added right now (details.retry_after_seconds). A start that would join a full start queue gets it at once, with retry_after_seconds, limit (queued_starts, or organization_queued_starts for the organization's share) and limit_value. Nothing was started; a suspended workspace stays suspended. details: reason, waited_seconds, required, pool, and deadline_seconds, or retry_after_seconds when it fails early. Send the same request again, after retry_after_seconds when it is set.
invalid_resources no The start asks for more CPU, memory or disk than the largest workspace size (details.reason: exceeds_host_class, required, host_class, exceeded). Open with smaller caps.
startup_failed yes A start command or service of the template failed (details.reason: startup_failed, service_not_ready, secrets_unavailable, secret_not_available, guest_feature_unavailable). The workspace keeps running for inspection; startup names the step, exit code and output tail. Fix the cause; the next open runs the failed step again.
workspace_storage_unavailable retryable says The workspace's storage cannot be reached. With details.reason: host_lost: its host failed (details.workspace_state: suspended; the workspace stays suspended). Retryable, with details.retry_after_seconds, while its disk is being moved to a new host; not retryable when neither its disk nor a checkpoint could be restored. See If a workspace's host fails. Otherwise the storage is out of reach for now. host_lost, retryable: resume again after retry_after_seconds. Not retryable: contact support with the workspace ID; a later resume restores the workspace once its disk is available again. Otherwise send the request again later.
restore_stalled yes The resume did not finish within 10 minutes. The workspace stays suspended with its files and its latest checkpoint (details.workspace_state: suspended, checkpoint_id, retry_after_seconds). Resume the workspace again after retry_after_seconds.
checkpoint_capture_failed yes The checkpoint could not be captured. With details.reason: snapshot_prepare_timeout, preparation exceeded its time budget; with checkpoint_space_unavailable, the suspend did not start because its checkpoint could not be written right now (details.retry_after_seconds). Either way the workspace keeps running with its memory and processes, and its previous checkpoint is kept. Continue working; retry the suspend later with backoff (after retry_after_seconds when it is set).
suspend_failed no The suspend could not complete and the workspace could not be put back to running (details.workspace_state: failed). Its disk is kept. Open its key or resume it: the start boots its disk again, with every process restarted.
resume_required no A fork or snapshot of a workspace that was suspended when its host failed and has not run since. Its newest files are on its disk, which the next resume boots. Resume the workspace (or open its key), then fork or snapshot it.
template_unavailable no The workspace's template version could not be installed for the start, or a layer of it did not verify against the version's manifest (details.reason: template_layer_unverified). Nothing was started; a suspended workspace stays suspended with its files. Open new workspaces on another template version; contact support with the workspace ID to start this one.
layout_unsupported no The template version's disk layout is not supported. Use another template version.
dependency_failed no The operation depended on one that failed (for example a draft capture behind a test instance). Check the other operation.
layer_capture_failed no Capturing a draft state or a template source failed. Retry the capture.
workspace_active no A suspend when idle found the workspace in use when it was due (a tool call after the request, or an attached stream; details.reason: tool_call_after, stream_attached, tool_in_flight, active_within). The operation is canceled; nothing changed and the workspace keeps running. OperationFailedError.workspaceActive (TypeScript 0.12.0+), .workspace_active (Python 0.8.0+). Nothing: it is not a failure. Ask again at the end of the next turn.
suspend_canceled no The suspend was canceled before its capture (shard operations cancel, cancelSuspend(), cancel_suspend(), workspace_suspend_cancel). The operation is canceled; the workspace keeps running as it was, with its memory and processes. Nothing: carry on in the workspace.

Durable copy of a suspend

A suspend succeeds once the workspace is sealed on its host; its checkpoint then reaches durable storage (Durable storage). The suspend operation stays succeeded in every case; its result.durability.state tells the rest.

durability.state Meaning What to do
pending (with overdue_at) The copy is taking longer than durable_by; it keeps going. Nothing, or keep waiting with durable: true.
lost The host was lost before the copy committed (reason, for example host_gone). The previous durable checkpoint stays the recovery point, and the next resume restores it and reports lost_suspend. With no earlier checkpoint, that resume fails with checkpoint_not_found. Redo the work done since lost_suspend.state_as_of.

Waiting for the copy (suspend({ durable: true }), waitForDurable(), suspend(durable=True), wait_for_durable()):

  • DurabilityLostError (TypeScript 0.12.0+, Python 0.8.0+): durability.state became lost. It carries the operation and durability (with reason).
  • OperationTimeoutError with durable: true: the wait's timeoutMs / timeout ran out while the copy was pending. The suspend has succeeded and the copy continues; wait again with waitForDurable(err.operationId) / wait_for_durable(err.operation_id).
  • The CLI (--durable, 0.7.0+) and the MCP server (durable: true, 0.6.0+) report both as errors naming the operation and durability.state: code durability_lost (details.state lost, details.reason; CLI exit 6) and code timeout with details {durable: false, state: "pending"} (CLI exit 5; keep waiting with shard operations wait <id> --durable or operation_wait with durable: true).

While an operation is capacity_pending, error.details shows where it stands: queue_position (its place in line), deadline_at (it moves later while new capacity starts) and pool.scale_out (requested while new capacity is starting, with pool.expected_ready_by).

Operation state_reason values are progress, not errors: no_ready_host while capacity_pending (waiting for a host), storage_host_unavailable while capacity_pending (the workspace's disk is being reattached; the start continues by itself), preserving_files while capacity_pending (the workspace's files are being moved to new storage; the start continues by itself once they are, and its deadline_at is an hour after its creation), lease_renewing while capacity_pending (the host is picking up the organization's current usage allowance; the start continues by itself), template_downloading while running (the host is fetching the template).

Template builds from a workspace or a draft report failure.code on the build: source_unavailable (the capture failed or the checkpoint is gone), resource_limit (details.limit: template_org_bytes_max or template_file_entries_max), scan_failed (the credential scan found secrets: the paths are listed; build again with acknowledged_scan_findings), compatibility_failed and publish_failed.

Builds of a template with immutable paths, from any source, also report:

failure.code Meaning What to do
immutable_path_missing An immutable path is not a directory in the built filesystem (details.path): it is missing, a file or a symbolic link. Create the directory in the build, with a files entry or a step.
immutable_image_too_large The immutable paths hold more than 16 GiB (details.value, details.actual). Keep large data in the template's other directories.

Client errors

The clients add their own errors for failures that never reached the API:

  • TypeScript SDK: ExecStartError (0.10.0+: a command that could not start; a ShardfluxApiError with exec_failed_to_start), OperationFailedError, OperationTimeoutError, DurabilityLostError (0.12.0+), ShardfluxProtocolError, ToolArgumentError, CheckoutTimeoutError (0.9.0+) and the template errors (reference). NotSupportedForModeError and TreeRevisionMismatchError (0.9.0+) are ShardfluxApiErrors for not_supported_for_mode and tree_revision_mismatch; the SDK raises the first before sending a call the mode does not have (local: true).
  • Python SDK: the same classes under ShardfluxError, with CheckoutTimeoutError, NotSupportedForModeError and TreeRevisionMismatchError from 0.5.0, ExecStartError from 0.6.0 and DurabilityLostError from 0.8.0 (reference).
  • CLI: exit codes and local codes such as usage, interrupted and (0.5.0+) checkout_ended and execution_failed (an execution that ended failed or lost, exit 6) (reference).
  • MCP server: invalid_arguments, workspace_pinned, timeout, operation_failed and others. An error result carries the refusal's reason, and from 0.4.0 a hint for the file-first and host refusals (reference).

Do not infer workspace deletion from 404

A bodiless/proxy 404 is a protocol failure. Even a structured not_found can mean a stale route or inaccessible scope. TypeScript 0.12.0 and Python 0.8.0 protocol errors include the responding source. isWorkspaceGone(err) / is_workspace_gone(err) is true only for an explicit API conflict with reason workspace_deleted. It is never true for a protocol error or cell not_found. Do not delete user data based on HTTP status alone.

failed is recoverable through resume or auto-wake with the same workspace ID and disk. It is not a tombstone. The workspace's failure carries the error of the operation that failed it (for example vm_start_failed with details.host_reason) until a start succeeds. Conditional idle suspension that loses the race to work is canceled before capture (workspace_active), leaving the workspace running.

Retention validation

422 validation_failed, details.reason: retention_requires_persistent means retention was requested for a session workspace. Use a persistent workspace to keep its state and have it deleted after 1 through 3650 idle days. Day values outside that integer range are 422 validation_failed. A retention setter on a deleted workspace returns 409 conflict, details.reason: workspace_deleted; open its key to start a fresh workspace.

Missing files

TypeScript SDK 0.16.0+ raises FileNotFoundError, a ShardfluxApiError, when a requested file path is missing. Python 0.12.0+ exposes shardflux.FileNotFoundError, inheriting both builtin FileNotFoundError and ShardfluxApiError. Catch it to create a file or ask for another path. Workspace deletion and protocol errors remain separate errors.

Tunnel replay

A cell conflict with details.reason=tunnel_replay_unavailable means the tunnel's retained output is unavailable. Open a fresh forward connection. Reverse tunnel handles in TypeScript SDK 0.27.3+, Python 0.24.0+ and CLI 0.24.1+ replace the listener automatically; their existing TCP peers reset.

View this page as Markdown