Skip to content

Every error code, and what to do about it

Grouped by status, with the details each one carries, which of them tell you exactly how long to wait, and which are safe to retry.

Updated View as Markdown

Every error from every route has one shape:

{
  "error": {
    "code": "account_not_found",
    "message": "…",
    "status": 404,
    "requestId": "…"
  }
}

Branch on error.code. Messages are written for humans and may change; codes are the contract. Keep requestId when reporting a problem — it comes back as the x-request-id response header too, and it is what your operator will ask for.

Some errors carry a fifth field, error.details, naming the specific thing that failed:

{
  "error": {
    "code": "scope_denied",
    "message": "…",
    "status": 403,
    "requestId": "…",
    "details": { "required": ["sign"] }
  }
}
Code details
scope_denied required — an array of every scope the route wanted, not just the one you lack
invalid_slug expected — the grammar it wanted, and the reserved names when that is why it was refused
account_locked account
limit_not_raised limit — which ceiling you named — plus current and requested
session_capacity_exceeded scope and limit — which ceiling you hit, and what it is
mint_rate_limited, account_unlock_rate_limited, workspace_creation_rate_limited, workspace_recreation_cooldown, admin_rate_limited retryAfterSec

Treat details as present-or-absent: no other code carries it, and nothing outside this table is exposed.

Wait exactly as long as you are told

Every 429 that knows its own deadline sets a Retry-After response header, in seconds. That covers the five rate-limit codes above. Read the header rather than guessing at a backoff — it is the authoritative answer, and for workspace_recreation_cooldown a guess will be badly wrong.

The capacity code session_capacity_exceeded does not set it: its ceiling clears when other work finishes, not on a clock. That is the one to back off exponentially.

400 — the request was malformed

Code Meaning and fix
invalid_body The JSON shape or field combination is wrong. The message names the expected shape and the fields that failed — … — "count" is required, … — unexpected field "tag". Field names only; your values are never echoed back.
invalid_slug A workspace or account identifier the service will not accept. Usually syntax — lowercase letters, digits, hyphens, starting alphanumeric. Workspace slugs additionally refuse four names reserved as directory names — con, prn, aux, nul — even though they satisfy that rule; account slugs do not, because they are generated rather than chosen. The details.expected says which of the two you hit.
invalid_parameter A path or query selector is malformed — for example ?force= or ?vm= with an unaccepted value. Also returned when the signing engine itself rejects a payload the service forwarded, such as typed data whose field types do not check out, or an address that fails its checksum.
chain_id_mismatch A typed-data domain.chainId names a different chain than the one being signed for. The message gives both numbers. See Sign.
invalid_mnemonic The supplied mnemonic could not be parsed.
invalid_private_key The supplied private key could not be parsed.
unsupported_network The network is not one the workspace supports. Returned both when the signing engine rejects a network and when a route’s ?network= selector (for example GET /v1/workspace/assets) names a well-formed network the workspace lacks.

401 — you are not authenticated

Code Meaning and fix
bad_api_key X-Api-Key or X-Api-Secret is missing or wrong.
bad_password A password was wrong — the workspace password when minting a token, or the account password when unlocking an account. Branch carefully: the same code covers both.
session_expired The idle or absolute clock elapsed. Mint a new token and retry — this is routine, not exceptional. Also returned for a missing or malformed Authorization header, and after a service restart, so it does not by itself mean a clock ran out.
admin_denied The super-admin X-Admin-Key is missing or invalid.

403 — authenticated, but not allowed

Code Meaning and fix
scope_denied The token is valid but lacks the scope. Re-mint with the scope you need.
permission_denied The operation is not permitted for this caller.

404 — no such thing

Code Meaning and fix
workspace_not_found No workspace with that slug for this tenant.
account_not_found No such account, wallet, or address in this workspace.
not_found The route or resource does not exist.
tenant_not_found A super-admin route named a tenant id that does not exist.

409 — conflicts with current state

Code Meaning and fix
workspace_exists That slug is already in use.
workspace_in_use The workspace is held. On delete that means live token leases, or a workspace caught mid-open or mid-close; ?force=true overrides the refusal in every one of those forms. It is also raised on create, when that slug is already being held — POST /v1/workspaces takes no force, so there retrying once it settles is the only option.
quota_workspaces_exceeded Tenant workspace limit reached.
quota_wallets_exceeded Tenant wallet limit reached.
duplicate_identity An add collided with an identity the workspace already holds — most often re-importing a private key that is already present. Distinct from invalid_parameter, which is for malformed input.
wallet_disabled A signing request named a wallet whose derivation slot is disabled. Disabling is a reversible marker the engine supports; this service does not currently expose a route to toggle it, so in practice you will not see this.
limit_not_raised A super-admin limit change named a value below the current ceiling. Limits move up only; a value equal to the current ceiling is accepted and changes nothing. details carries the field, its current value, and what you asked for.

422 — well formed, but cannot be done

Code Meaning and fix
unsupported_for_kind The account, wallet, or chain type cannot perform this otherwise valid operation — typed data on a non-EVM address, a mnemonic from a PK account, a VM the wallet lacks.
weak_password The password is shorter than 12 characters. Length is the only rule — no character-class requirement applies. Raised when creating a workspace or an own-password account, never when minting a token.

423 — locked

Code Meaning and fix
workspace_locked The workspace is not open. Mint a token.
account_locked The account is not currently open. Call the unlock route with the account’s password — or, for an account that shares the workspace password, with the workspace password. Any account reaches this state once its custody window expires, not only one created with hasOwnPassword.
record_locked A record is held by another operation. Retry shortly.

429 — slow down, or you are at capacity

Code Meaning and fix
mint_rate_limited Too many token mints for this tenant. Back off. Account unlock spends the same tenant budget, so this also answers an unlock.
account_unlock_rate_limited Too many failed account-unlock attempts.
workspace_creation_rate_limited Creating workspaces too quickly.
workspace_recreation_cooldown That slug was recently deleted and is still cooling down.
session_capacity_exceeded A capacity ceiling was hit. details.scope says which: tenant (workspaces open at once), process, or workspace (live token leases on one workspace, default 64). Revoke tokens you no longer need — a client that mints per request without revoking reaches the workspace ceiling with only one workspace open.
admin_rate_limited Too many super-admin requests. Back off; details.retryAfterSec says how long.

500 — the service failed

Code Meaning and fix
storage_error A storage operation failed.
encrypt_failed Encryption failed.
decrypt_failed Decryption failed.
algorithm_irreversible The requested transformation cannot be reversed.
storage_not_durable A write could not be confirmed durable. Do not assume it landed — re-read before retrying anything that mutates.

These are not your fault and not fixable by retrying the same request. Capture requestId and report it.

501 — not implemented

Code Meaning and fix
unsupported_operation The operation is not supported by the underlying engine — most often a /sign/message encoding the address cannot perform, such as ed25519 on an EVM address or personal_sign on a Solana one. See Sign.

507 — capacity

Status Code Meaning and fix
507 disk_full The service is out of storage.

Retry guidance

Situation Retry?
429 with a Retry-After header Yes, after exactly that long
429 without one Yes, with exponential backoff
session_expired Yes, after minting a new token
record_locked Yes, shortly
Any 400, 403, 404, 422 No — fix the request
500-class No — report with requestId

Codes from outside the catalog

The codes above are the service’s own, and each one names a specific condition. A few others come from the layer in front of it, before a request ever reaches a route:

Status Code When
400 bad_request The JSON body could not be parsed at all
401 unauthorized A rejection with no more specific code
403 forbidden A rejection with no more specific code
404 not_found No such route. Note this is the same code a missing record produces, so the two are not distinguishable on the wire
413 payload_too_large The request body exceeded the parser’s limit, roughly 100 KB
500 internal_error An unhandled failure. Capture requestId and report it
— request_failed A 4xx with no more specific code

They carry the same envelope as everything else, so branching on error.code still works — but they are generic by construction, and error.details is never present on them.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close