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 — the scope the route wanted |
invalid_slug |
expected — the grammar it wanted |
account_locked |
account |
rpc_not_configured |
network |
session_capacity_exceeded, rpc_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 |
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 four 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 two capacity codes, session_capacity_exceeded and
rpc_capacity_exceeded, do not set it: their ceilings clear when other
work finishes, not on a clock. Those are the ones 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. |
invalid_slug |
A workspace or account identifier has invalid syntax. Lowercase letters, digits, hyphens; starts alphanumeric. |
invalid_parameter |
A path or query selector is malformed — for example ?force= or ?vm= with an unaccepted value. |
invalid_mnemonic |
The supplied mnemonic could not be parsed. |
invalid_private_key |
The supplied private key could not be parsed. |
unsupported_network |
The signing engine cannot work with that network. Note this is not what a route’s ?network= selector returns for a network the workspace lacks — that is a 404 account_not_found. |
rpc_endpoint_rejected |
The RPC URL you tried to register was refused at admission. |
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 |
The workspace password is wrong. |
session_expired |
The idle or absolute clock elapsed. Mint a new token and retry — this is routine, not exceptional. |
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. |
export_disabled |
No exportPublicKey is registered for the tenant. Your operator must register one. |
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, address, or network in this workspace. |
not_found |
The route or resource does not exist. |
409 — conflicts with current state
| Code | Meaning and fix |
|---|---|
workspace_exists |
That slug is already in use. |
workspace_in_use |
A session is open. Retry with ?force=true if revoking it is intended. |
quota_workspaces_exceeded |
Tenant workspace limit reached. |
quota_wallets_exceeded |
Tenant wallet limit reached. |
rpc_not_configured |
No usable endpoint for that network. Register one, or ask your operator. |
tx_dropped |
The transaction was dropped before inclusion. |
tx_aborted |
Submission was aborted before it completed. |
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. |
tx_build_failed |
The transaction could not be constructed from the supplied fields. |
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_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 |
Too many workspaces held open at once. Revoke a token you no longer need. |
rpc_capacity_exceeded |
Too many concurrent chain operations. Retry with backoff. |
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. |
tx_sign_failed |
Signing did not produce a usable transaction. |
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 |
|---|---|
not_implemented |
The route exists but is not available in this release — see Addresses. |
unsupported_operation |
The operation is not supported by the underlying engine. |
502, 504, 507 — upstream and capacity
| Status | Code | Meaning and fix |
|---|---|---|
| 502 | rpc_unreachable |
The RPC endpoint did not respond or answered non-2xx. |
| 502 | rpc_rejected |
The endpoint responded with a refusal. |
| 502 | tx_submit_failed |
The endpoint explicitly refused the submission. |
| 504 | tx_timeout |
Submission timed out. Check transaction status before resending. |
| 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 |
rpc_unreachable, rpc_rejected |
Yes, with backoff; consider a different endpoint |
tx_timeout, or send returning unknown |
Not on found: false alone — that cannot distinguish “never sent” from “in the mempool”. See Send transactions |
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 — as distinct from no such record |
| 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.
Related
- API conventions — the error shape and request tracing
- Safe usage