Errors
Every code the API sends, whether sending the same request again can succeed, and what to do instead. Agents: branch on code and retryable, never on the sentence.
The error envelope
{"error": "<message>", "code": "<CODE>", "retryable": false, "docs_url": "https://innstance.impossibuild.ai/docs/errors#<CODE>"}erroris the human sentence. Print it as-is; clients that read only this field keep working.codeis stable once sent: branch on it, never on the sentence. A refusal with no code of its own carries the generic code for its HTTP status.retryableis true when the same request, sent again after a pause, can succeed with nothing changed. HonourRetry-Afterwhen it is set.docs_urllinks to the code on this page.
Some codes add fields of their own (upgrade, retry_after, over_pool_until, moderation). Codes are case-sensitive; a few early subscription codes are lower case and stay that way. A body over a route's size cap is refused with a plain-text 413 before any handler runs.
Machine-readable: /docs/errors.json · /docs/errors.md · /openapi.json
Error codes
| Code | HTTP | Retryable | Meaning | What to do |
|---|---|---|---|---|
INVALID_REQUEST (generic) | 400 | no | The request is malformed or a value is not allowed. | Read the message: it names the field and the rule. Fix the request and send it again. |
UNAUTHENTICATED (generic) | 401 | no | No credential, or the token is unknown, revoked or expired. | Sign in again (innstance login) or send a valid token as Authorization: Bearer <token>. |
PAYMENT_REQUIRED (generic) | 402 | no | The request needs payment or more balance first. | Pay or top up as the body describes (x402 challenges carry an accepts list), then send the request again. |
FORBIDDEN (generic) | 403 | no | This account may not do this. | Do not retry. The message says why; a cookie request from another origin is refused here too. |
NOT_FOUND (generic) | 404 | no | No such resource on this account (another account's resources also answer 404). | Check the name; list what exists (GET /apps, /sites, /agents/hosts). |
REQUEST_TIMEOUT (generic) | 408 | yes | The request was cancelled before the operation finished. | Read the resource's state, then send the request again if it did not take effect. |
CONFLICT (generic) | 409 | no | The request conflicts with the resource's current state: a name already taken, a limit on tokens, a resource in the wrong state. | Change the request or the resource as the message says; waiting alone does not clear it. A conflict that does clear by itself, after which the same request passes, has its own retryable code (OPERATION_IN_PROGRESS, POOL_LOCKED, DEPLOY_IN_PROGRESS, APP_OPERATION_IN_PROGRESS). When the work asked for is already under way (an assistant being set up), watch the resource instead of sending the request again. |
GONE (generic) | 410 | no | What the request refers to existed but has expired or been used. | Start the flow again to get a fresh link, token or session. |
PAYLOAD_TOO_LARGE (generic) | 413 | no | The request body is larger than this route accepts. | Send less. JSON routes take up to 64 KiB; upload routes state their plan's limit in the message. |
UNPROCESSABLE (generic) | 422 | no | The request is well formed but its content cannot be accepted. | Change the content as the message says. |
RATE_LIMIT (generic) | 429 | yes | Too many requests from this account or address. | Wait for Retry-After seconds (also in retry_after or retry_after_seconds when present), then send the request again. |
INTERNAL_ERROR (generic) | 500 | yes | The server failed while handling the request. | Retry with backoff. For a write, read the resource first: part of it may have happened. |
UPSTREAM_ERROR (generic) | 502 | yes | A provider the platform depends on (machines, certificates, payments, DNS) failed or answered something unusable. | Retry with backoff. Failures whose outcome is unknown use OUTCOME_UNKNOWN instead. |
UNAVAILABLE (generic) | 503 | yes | A dependency is briefly unavailable, or coordination with another request failed. | Retry after a short pause. A feature this deployment does not run answers NOT_ENABLED instead. |
TIMEOUT (generic) | 504 | yes | The operation ran out of time. | Read the resource's state, then retry if it did not finish. Work run on a machine, which may still be running there, answers EXEC_TIMEOUT instead. |
OPERATION_IN_PROGRESS | 409 | yes | Another change to the same app, site or assistant is running, or it is still starting. | Wait a few seconds and send the same request again. |
NOT_ENABLED | 503 | no | This deployment does not run the feature (it is switched off in its configuration). Sent by: assistants and their settings changes, backups, volumes, custom domains, legacy CLI login, card checkout, traffic top-ups, deploys without a runner image. | Do not retry; use another way, or ask the operator. A few older messages call this "temporarily unavailable"; the code is what holds. |
OUTCOME_UNKNOWN | 500, 502, 503 | no | The request may already have taken effect, in whole or in part, and the server cannot confirm it. Sent by: app create, commands run with exec, runner upload completion, subscription plan changes, x402 top-ups whose settlement did not answer or answered something unusable. | Do not repeat it blindly: read the target's state first (the message says what to check), then decide. |
ACCOUNT_SUSPENDED | 403 | no | The account is suspended for breaking the acceptable use policy. Sent by: every sign-in route. | Do not retry. The body carries the reason and an appeal link when there is one. |
ACCOUNT_CHANGED | 403 | no | The browser session now belongs to a different account than the page expected. | Reload the page and confirm the action with the intended account. |
TOKEN_REQUEST_REPLAY | 409 | no | This token request already created a token; its secret cannot be shown again. Sent by: POST /auth/token. | Check GET /auth/tokens and revoke the token if its secret was not received. |
RATE_LIMIT_UNAVAILABLE | 503 | yes | The rate limiter could not record the request, so an expensive route refused it. | Retry after a short pause. |
FREE_TIER_DISABLED | 403 | no | New free accounts are switched off on this server. | Subscribe to a paid plan (upgrade names the cheapest). |
FREE_TIER_FULL | 403 | no | The free plan has reached its account cap. | Subscribe to a paid plan. |
APP_LIMIT | 403 | no | The plan's app count is reached. Sent by: app create. | Remove an app, or upgrade to the plan named in upgrade. |
SITE_LIMIT | 403 | no | The plan's published-site count is reached. Sent by: site create. | Remove a site (innstance sites rm), or upgrade to the plan named in upgrade. |
CPU_LIMIT | 403 | no | More CPUs per machine than the plan allows. Sent by: app create and update. | Ask for fewer CPUs, or upgrade. |
CPU_KIND_LIMIT | 403 | no | Performance CPUs are not included in this plan. Sent by: app create and update. | Use cpu_kind shared, or upgrade. |
POOL_MEMORY_LIMIT | 403 | no | The change would take the account over its plan's RAM pool. Sent by: app create and update, scale, assistant launch. | Free RAM (smaller machines, fewer apps or assistants), or upgrade. |
POOL_VOLUME_LIMIT | 403 | no | The change would take the account over its plan's storage pool. Sent by: volume create and extend, assistant launch. | Remove or shrink volumes, or upgrade. |
VOLUMES_NOT_AVAILABLE | 403 | no | The plan does not include persistent volumes. Sent by: volume create. | Upgrade, or keep state in a managed database. |
AUTOSCALE_LIMIT | 403 | no | More machines per app than the plan allows. Sent by: app create and update, scale. | Ask for fewer, or upgrade. |
DOMAINS_NOT_AVAILABLE | 403 | no | The plan does not include custom domains. Sent by: domain add. | Upgrade. |
DOMAIN_LIMIT | 403 | no | The plan's custom-domain count is reached. Sent by: domain add. | Remove a domain, or upgrade. |
ALWAYS_ON_PERFORMANCE | 403 | no | The change would add always-on performance CPU, which this account's plan does not include. Sent by: deploy, apply, scale. | Follow one of the fixes in the body. |
ALWAYS_ON_COST_LIMIT | 403 | no | The change would take the account's always-on machines past the plan's always-on budget. Sent by: deploy, apply, scale. | Follow one of the fixes in the body. |
LIMITS_UNAVAILABLE | 403, 503 | yes | Usage could not be measured, so a change that needs the pool check was refused. | Retry in a moment. |
OVER_POOL | 402 | no | The account is over its plan's pool; over_pool_until is when the grace period ends. | Reduce usage or upgrade before making further changes. |
POOL_LOCKED | 409 | yes | Another change to this account's pool is being written. | Retry with a short jittered backoff; the CLI does this itself. |
TRIAL_EXPIRED | 403 | no | The free trial for this app or site has ended. Sent by: deploy, site upload. | Upgrade (url in the body) to deploy or publish again. |
SETUP_LIMIT | 409 | no | Too many names are held by pages or assistants that were never finished. Sent by: site create, assistant launch. | Finish or remove one of them, then try again. |
NAME_TAKEN | 409 | no | The name is taken on the public namespace. | Pick another name. |
NAME_NOT_ALLOWED | 400 | no | The name resembles a well-known brand or the platform, or promotes gambling, adult content or scams. | Pick a plainly different name; the reply never says which rule matched. |
agent_name_yours | 409 | no | This account already has an assistant with this name; status says what state it is in. Sent by: POST /agents. | Carry on with it (send its inputs), or destroy it first. |
agent_name_taken | 409 | no | The name is held by another account, or by one of this account's sites or apps. Sent by: POST /agents. | Pick another name. |
UNDER_REVIEW | 403 | no | The resource is held or quarantined by trust and safety. | Do not retry; it cannot be changed or deleted until reviewed. moderation carries the reasons and the appeal link. |
FILE_TYPE_NOT_ALLOWED | 422 | no | The free plan publishes only web file types; files lists the refused ones. Sent by: site upload. | Remove those files, or upgrade. Nothing was published. |
IMAGE_NOT_CHECKABLE | 422 | no | An image could not be checked (AVIF, HEIC, JPEG XL, or far larger than a page needs). Sent by: site upload. | Save it as PNG, JPEG, GIF or WebP. Nothing was published. |
DEPLOY_IN_PROGRESS | 409 | yes | A deploy is already running on this app. Sent by: runner deploy, app delete. | Wait for it to finish (GET /apps/{name}/deployments), then retry. |
APP_OPERATION_IN_PROGRESS | 409 | yes | Another deploy or app-wide operation is running. | Retry when it finishes. |
DELETE_FAILED | 502 | yes | Tearing the app down at the provider failed; the app is kept as delete_failed. | Call DELETE again (retry carries a hint). |
EXEC_TIMEOUT | 504 | no | The command ran out of time. It may still be running on the machine, or have finished there. Sent by: POST /apps/{name}/exec, POST /agents/hosts/{name}/exec, POST /apps/{name}/pull (archiving). | Do not send the same request again: it would run the work a second time and run out of time the same way. Start a long command detached (nohup <cmd> > /tmp/log 2>&1 < /dev/null &) and poll it with short commands; innstance agents ssh has no time limit. For a backup, choose a smaller directory. |
EXEC_OUTPUT_TOO_LARGE | 502 | no | The command ran, but its output was larger than an exec response can carry. Sent by: POST /apps/{name}/exec. | Do not run it again as is: redirect its output to a file and fetch the file (innstance machines pull). |
DOMAIN_PROOF_REQUIRED | 428 | yes | Ownership of the hostname is not proven yet; nothing was written. Sent by: domain add (apps and sites). | Publish the TXT record in verify (and the CNAME in records), then send the same request again. |
PAYMENT_NOT_CREDITED | 500 | no | The payment settled on-chain, but the account could not be credited with it. Its transaction (tx_hash) is kept for reconciliation. Sent by: x402 top-ups (GET /subscription/topup/x402, GET /billing/egress/topup/x402). | Do not pay again: the same request cannot succeed, and a fresh payment would be a second one. Check GET /subscription/topup/recent (plan days) or GET /billing/usage (traffic); if the credit has not appeared, contact support with tx_hash. |
PRICING_NOT_ON_SALE | 409 | no | There is no other pricing version on sale here, so the account's plan is the one on sale. Sent by: POST /billing/pricing. | Nothing to do. |
PRICING_SWITCH_NOT_ALLOWED | 409 | no | A switch is one way: a pricing version that is no longer sold cannot be chosen again. Sent by: POST /billing/pricing. | Nothing to do; the account keeps its version. |
PRICING_SWITCH_DOES_NOT_FIT | 409 | no | The account does not fit the new version's plan yet; blockers lists what stands in the way. Nothing was changed. Sent by: POST /billing/pricing. | Act on each blocker, check again with GET /billing/pricing, then send the switch again. |
PRICING_VERSION_CHANGED | 409 | no | The account's plan changed while it was switching; nothing was written. Sent by: POST /billing/pricing. | Read GET /billing/pricing again and switch only if its preview still holds. |
checkout_in_progress | 409 | yes | A checkout is already open for this account. | Finish or abandon it, then retry. |
crypto_subscription_active | 409 | no | An active crypto subscription must be cancelled before a checkout. Sent by: POST /subscription/checkout. | Cancel it first. |
already_on_plan | 409 | no | The account is already on the requested plan. | Nothing to do. |
cancel_first | 409 | no | The account has a different recurring plan. Sent by: POST /subscription/checkout. | Switch with POST /subscription/change-plan, or cancel first (the paid period is kept). |
card_coming_soon | 503 | no | Card checkout is not available on this deployment. | Pay with USDC instead (innstance billing topup). |
scheduled_change_active | 409 | no | A plan change is already scheduled; scheduled_tier and scheduled_change_at say which and when. | Call it off (POST /subscription/change-plan/cancel) before scheduling another. |
not_configured | 503 | no | Plan changes need the payment gateway, which this deployment lacks. Sent by: subscription plan changes. | Do not retry. |
no_subscription | 409 | no | There is no active recurring subscription to change. | Subscribe with POST /subscription/checkout instead. |
billing_state_mismatch | 409 | no | The platform's record of the plan and the payment gateway's disagree. | Do not retry; contact support to reconcile billing. |
change_reconciliation_pending | 409 | yes | A previous plan change is still being reconciled with the gateway. | Retry once it has landed (a few minutes). |
payment_declined | 402 | no | The upgrade charge was declined. | Check the payment method, or cancel and subscribe to the new plan. |
no_scheduled_change | 409 | no | No plan change is scheduled. | Nothing to call off. |
change_not_cancelable | 409 | no | The scheduled change is already being applied. | Pick a plan again once it has landed. |
payments_paused | 503 | yes | USDC payments are paused on this server. Nothing was charged. | Retry after the time in Retry-After, or pay by card where the plan offers it. |
PERFORMANCE_ADDON_LIMIT | 403 | no | This change needs more performance machines than the account's add-ons cover, one add-on per machine. | Use shared CPU, or buy the add-on the message names (innstance billing addons). |
unknown_addon | 400 | no | There is no add-on with that ID. | Use an ID from the message, or from GET /billing/plans. |
addon_not_found | 404 | no | The account holds no active add-on in that slot. | List the account's add-ons (innstance billing addons). |
addon_not_for_plan | 403 | no | This plan already includes performance CPU, so the add-on would add only its RAM. | Do not buy it on this plan; the message says where it is sold. |
addon_needs_paid_plan | 403 | no | The add-on is for paid plans. | Subscribe to the plan the message names, then buy the add-on. |
addon_not_on_sale | 503 | no | This add-on cannot be bought on this server yet. | Do not retry. GET /billing/plans says what is on sale. |
addon_does_not_renew | 409 | no | This add-on was a one-time purchase, so there is nothing to cancel. | Nothing to do: it ends on the date in ends_at. |
addon_slot_ambiguous | 409 | no | Several renewing add-ons match. | Send the request again naming one slot from slots. |
UPLOAD_IN_PROGRESS | 409 | yes | Another request is uploading this file right now. | Wait for it, then finalize. |
UPLOAD_ALREADY_RECEIVED | 409 | no | This file was already uploaded to the session; each upload URL takes one upload. | Finalize, or start a new session to send different bytes. |
UPLOAD_INCOMPLETE | 409 | no | Some of the session's files have not been uploaded. | PUT the files listed in missing to their upload_url, then finalize again. |
CLI exit codes
| Exit status | Meaning |
|---|---|
| 0 | Success. domains check exits 0 only when the domain is configured and the site answers. |
| 1 | The command failed; the reason is printed to stderr as Error: <message>. This covers API errors (the message carries the HTTP status, e.g. API error (409): ...), invalid flags or arguments (including those machines wait-for --help lists under 2), and a --wait-for-match or machines wait-for timeout. domains check also exits 1, after printing its report, when the domain is not ready yet. |
| 2 | domains check only: the check was rate-limited (HTTP 429). Run it again after the printed number of seconds. |
| 1-255 (passed through) | machines exec and agents exec exit with the remote command's own status, so if ! innstance machines exec ... works in scripts; a status outside 1-255 becomes 1. agents ssh exits with the ssh client's status: the remote command's, or 255 when ssh itself fails. |
| 1-255 (a local program's) | A command that runs a program on your computer can exit with that program's status when the program fails. Today that is git: deploy with [secrets] in its config runs git ls-files to check the config is not committed, and a git failure other than "not tracked" (for example 128, detected dubious ownership) exits with git's status. The message on stderr says which check failed. |