Skip to Content
Error codes

Error codes

Every error response carries the envelope that the Getting started page describes. The error field is one of the codes below. We regenerate the table from our error catalog at every build. It lists the codes that an operator can observe.

Branch on the code, not on the message text. We never rename a code and we never change its meaning. We can add new codes without notice. If you receive a code that you do not know, act on the HTTP status class of the response.

The source field is an opaque value. Do not try to decode it. Quote it with the request_id in a support request.

A code with widget in the Status column never appears in an HTTP response. It is a state of the widget in the browser. We list it so that you can understand the widget states that your end users report.

StatusCodeWhen it firesRetry and action
400validation_failedThe request body or query is malformed. On the position routes it also fires for an unknown market or outcome, for a market that is not open, and for a request outside the trading window of the market. A suspended market returns market_suspended instead.no — fix the request; in the widget the market state may simply have moved, so refetch and re-render (22-trading-ux)
400idempotency_key_requiredA write request has no Idempotency-Key header.no — add the header and send a fresh request
401invalid_api_keyThe API key is unknown, revoked or expired. The response does not say which.no — rotate or re-issue the key via the admin panel (10-operator-api key_lifecycle), then retry
401invalid_session_tokenThe widget session token is missing, malformed, expired or revoked. The response does not say which.conditional. Refresh via POST /v1/widget/sessions/refresh and retry the original request with the same Idempotency-Key. If the refresh call itself returns this code the session is unrecoverable and the host page must mint a new one (26-embeddable-widget)
403insufficient_scopeThe API key does not carry the scope that the endpoint needs.no — request the scope grant, then retry
403tenant_suspendedThe tenant is suspended. A suspension also revokes the API keys, so you usually see invalid_api_key instead.no — contractual, not technical; contact the platform
403user_blockedThe end user has status blocked. A blocked user cannot open or close a position and cannot place a limit order.no — the operator lifts the block via user upsert (11-user-balance-sync)
404not_foundThe resource id in the request does not exist.no
404unknown_userThe external_user_id has not been registered with a user upsert.no — upsert the user first (PUT /v1/users), then retry the original request
409would_overdrawA debit would take the virtual balance of the user below zero. Nothing is applied.conditional — only after the stake is lowered or the balance changes (22-trading-ux)
409position_terminalA close was requested for a position that already settled or was voided, or a close quote for one that already settled, was voided or was closed. Nothing is applied.no — the position is over. A position closed by mistake has no designed correction flow (41-admin-panel settlement_correction covers a market resolved to the wrong outcome, a market voided by mistake and a market resolved when it should have been voided, never a single closed position), and a retry does not fix it
409price_movedThe market price moved past the limit in the request. An open fails above max_price and a close fails below min_price. Nothing is applied.conditional — re-quote from the current price and re-confirm; an open re-confirm is a new intent with a new Idempotency-Key and a new max_price, a close re-confirm carries a new min_price (22-trading-ux)
409market_suspendedThe market exists but is suspended. It can reopen. Opening, closing and quoting a position and placing a limit order fail while it is suspended.conditional — retry once the market reopens; lifecycle events and the market feed signal the transition (13-realtime-pipeline, 27-market-feed)
409stake_limit_exceededThe stake is above the per-position cap of the market, or it would take the total open stake of the user on the market above the per-user cap. Nothing is applied.conditional — lower the stake to details.remaining or less; the per-user allowance otherwise frees at settlement, void, or when the user closes a position on that market (12-position-transaction-api closing_positions)
409state_conflictA request to cancel a limit order found the order already filled, expired or rejected.no — reload and decide on what the resource is now
409report_limit_reachedThe tenant already has the maximum number of report jobs in status generating. No job is created.conditional: poll GET /v1/reports/{id} or wait for a report.ready webhook, then retry once one of the tenant's jobs has left status generating. No Retry-After: the time a job takes is not known in advance
409saved_markets_limit_reachedThe end user already has the maximum number of saved markets, and the market in the request is not one of them. Nothing is saved. Saving a market that is already saved still succeeds.conditional: only after the user removes a saved market (DELETE /v1/widget/saved-markets/{market_slug}). A retry without that fails the same way, so the widget does not retry on its own
409idempotency_key_in_progressAn earlier request with the same Idempotency-Key is still being processed. Nothing is applied by this request. The response carries a Retry-After header.yes — honor Retry-After and retry with the SAME Idempotency-Key and body; once the earlier request has finished, the retry replays the position it opened, or is evaluated afresh if it opened none (a rejected open does not consume its key). After an earlier request whose outcome is unknown (a timeout, a dropped connection) it can last up to the 30 second reservation. A new key here would be a second intent and can open a second position
413request_too_largeThe request body is larger than the size limit of the route.no — shrink the body or split the batch
422idempotency_key_reusedThe Idempotency-Key was seen before with a different request body. Nothing is applied.no — a retry MUST reuse the original body byte-identical; a new intent needs a new key
429rate_limitedThe tenant or the end user exceeded the rate limit of the route. The response carries a Retry-After header.yes — honor Retry-After; write retries reuse the same Idempotency-Key, so a retried burst can never double-apply (19-virtual-ledger)
500internal_errorAn unexpected failure on our side.conditional — safe on idempotent writes with the same Idempotency-Key; if persistent, report the request_id and source
503sync_pausedWe paused the balance-sync endpoints with a kill switch.conditional — retry after the switch clears; queued syncs replay safely via idempotency
503positions_pausedWe paused the opening of positions with a kill switch.conditional — retry after the switch clears; widget shows the paused state meanwhile (22-trading-ux)
503feed_pausedWe paused the Market Feed reads with a kill switch.conditional — retry after the switch clears
503widget_pausedWe paused widget session mint, widget session refresh and the widget config read with a kill switch.conditional — retry after the switch clears
503market_not_readyThe state of the market is still rebuilding after a restart on our side. The response carries a Retry-After header.yes — honor Retry-After; write retries reuse the same Idempotency-Key, so a retried open can never double-apply
widgetrealtime_unavailableThe widget used all its reconnect attempts and now runs on the polling fallback.yes — automatic; the widget keeps attempting realtime recovery in the background
widgetwidget_session_expiredThe widget session token expired and the automatic refresh failed.yes — automatic refresh first; this state surfaces only when refresh itself fails
widgetwidget_config_invalidThe embed parameters failed the check at widget boot. The widget reports it to the browser console and shows nothing to the end user.no — the operator fixes the embed
widgetwidget_runtime_errorAn uncaught client-side exception in the widget. The widget reports it to us with its error beacon.no — beacon-reported for platform-side triage
Last updated on