# Error codes

Every error response has the shape
`{code, message, remediation, docs_url, request_id}`. Include the
`request_id` when reporting problems.

## INVALID_REQUEST

The request body or parameters failed validation. The message lists the offending fields.

## UNAUTHORIZED

Missing, malformed, expired, or revoked credential.

## FORBIDDEN

The credential is valid but lacks the required scope.

## AUTH_CHALLENGE_EXPIRED

The SIWE nonce is unknown, already used, or expired. Nonces are single-use.

## AUTH_SIGNATURE_INVALID

Signature verification failed, or the SIWE message fields do not match this environment.

## IDEMPOTENCY_CONFLICT

The Idempotency-Key was already used with a different payload.

## NOT_FOUND

The resource does not exist or does not belong to this account.

## RATE_LIMITED

Too many requests. Respect Retry-After; unauthenticated endpoints are limited per IP at the edge.

## LIMIT_EXCEEDED

A prepaid balance top-up was refused because it would push a platform cap: your per-account prepaid balance, or the platform-wide aggregate prepaid float. The message names the cap in dollars. This is a limit on customer float the platform will hold, not a payment error — nothing was broadcast and no authorization was consumed. Top up a smaller amount, or spend down existing balance; the caps are operator-adjustable.

## VENUE_UNAVAILABLE

No execution venue answered. Transient; retry shortly.

## POLICY_VIOLATION

The request exceeds what the wallet's active policy authorizes. The message names the violated rule.

## BUDGET_EXCEEDED

The spend would exceed a policy budget or velocity limit (see pre-flight POST /v1/checks).

## COUNTERPARTY_BLOCKED

The counterparty is on a screening list (e.g. OFAC SDN) and the policy requires screening.

## CONFLICT

The resource is in a state that forbids this transition (e.g. confirming a revoked delegation, or on-chain state moved since prepare). The message says which.

## UNAVAILABLE

A required backend capability is not configured in this environment.

## JURISDICTION_BLOCKED

Prediction-market surfaces are unavailable from this jurisdiction (venue-mirrored list; unknown origins are refused). Non-PM treasury features are unaffected.

## EXECUTION_DISABLED

Delegated prediction-market order routing exists but is switched off in this environment. Pre-flight checks and reads remain available.

## DEADLINE_EXCEEDED

The request spent its whole time budget waiting on third parties (venue APIs, RPC nodes) and stopped rather than wait longer. Returned as 504. Not the same as VENUE_UNAVAILABLE: a slow venue answered nothing wrong, and this was our scheduling decision, not theirs. The message says how much budget there was and what the handler refused to start; the remediation says whether anything was transmitted and names the read that confirms it. Nothing that moves funds is ever abandoned mid-flight — the budget is checked before a transaction is broadcast, never after, so a request that fails here either sent nothing or names the hashes it did send.


# Intent abort reasons

When the executor stops an accepted intent, the intent's `params.abort.reason`
and the `intent.aborted` event carry one of these codes. Partial results are
never discarded: fills that settled are always ledgered before the abort.

## UNSUPPORTED_KIND

The intent kind has no executor path yet. Delegated wallets also hit this when the delegation lives on the testbed chain, which has no venue — only gas top-ups (WETH unwrap) run there. POST /v1/delegations/prepare a grant on the execution chain to convert.

## EXECUTOR_UNCONFIGURED

Signing credentials are not bound in this environment; nothing was signed. This is an operator-side gap, not a request the caller can fix — retrying will fail identically until the environment is bound.

## NO_DELEGATION

The wallet has no active session-key delegation. POST /v1/delegations/prepare, then POST /v1/delegations/{id}/confirm the grant it returns.

## UNSUPPORTED_CHAIN

The pinned policy targets a chain this execution path does not serve (venues are mainnet-only; delegations run where granted). GET /v1/tokens lists the chains that are served; PUT /v1/policies to repin this wallet to one of them.

## POLICY_VIOLATION

Re-checked at the moment of execution and refused; the detail names the violated rule. Two different things land here and the detail distinguishes them: a rule in the wallet's own policy, which PUT /v1/policies changes, or 'outside the delegated scope', which means the on-chain grant does not authorize the call — a narrower gate the policy cannot widen. Re-grant with POST /v1/delegations/prepare for the latter.

## INSUFFICIENT_BALANCE

The wallet does not hold, on-chain, the TOKENS the intent sells. This is the wallet's own chain balance and NOT the prepaid fee balance that pays for API calls — that one refuses with a 402 or BUDGET_EXCEEDED and is refilled with POST /v1/balance/topup. Topping up fees does nothing for this abort, and vice versa. On a conversion: earlier partial fills or an outside transfer drained the position below the requested size, so re-read the wallet and submit a smaller POST /v1/intents. On a gas_topup: the wallet holds no wrapped native (WETH) to unwrap, so the automatic top-up that keeps it able to transact has nothing to work with — convert something the wallet DOES hold into WETH with POST /v1/intents, or send native funds to the wallet directly. The detail names the token, the balance held and the amount required.

## DEPTH_COLLAPSE

Live venue depth cannot support even two slices at the requested size. POST /v1/quotes to see what size the venues will actually price right now, then POST /v1/intents for that smaller size — this is a size problem, and retrying the same size will refuse identically.

## PRICE_GAP

Venue price diverged from the independent reference beyond twice the slippage budget. POST /v1/quotes to see the current spread; retry when it narrows, or PUT /v1/policies to widen execution.max_slippage_bps if the wallet genuinely accepts that price.

## VENUE_DISAGREEMENT

The venue's own quotes imply inconsistent pricing beyond ten times the slippage budget. POST /v1/quotes to observe the pair before resubmitting; this usually clears on its own within minutes and widening slippage is NOT the fix — the quotes are internally inconsistent, not merely wide.

## VENUE_NO_QUOTE

A venue could not price this pair at all — most often the pair is not routable there. Distinct from VENUE_DISAGREEMENT, where venues answered and their prices conflicted. GET /v1/tokens for the tradeable set and POST /v1/quotes to test the pair before committing an intent.

## UNSUPPORTED_TOKEN

One of the tokens is not in this chain's registry, so its decimals and stable-ness are unknown and it cannot be sized or priced safely. Use a supported token; GET /v1/tokens lists every token this platform accepts, per chain.

## VENUE_UNAVAILABLE

No venue could even be asked: none supports the chain, none survived the policy's execution.venues allowlist, or an operator has switched the last one off. The detail names each venue and its state. Distinct from VENUE_NO_QUOTE, where a venue was asked and declined to price. Read the detail before retrying: an allowlist that names no serving venue is a policy the caller fixes with PUT /v1/policies, while an operator switch-off is an outage that only time fixes.

## NO_PRICE_REFERENCE

Only one venue can fill AND no independent price reference (an on-chain oracle feed) covers this pair, so the price cross-check has nothing to compare against. Single-venue execution normally continues against an oracle reference; this is the one case that still refuses, because filling on a single unverified quote is worse than not filling. Not a retry: route through a pair that has a reference — GET /v1/tokens names the stables, and a leg through one of them is covered.

## SPEND_CAP_EXCEEDED

The wallet's own policy.spend caps refuse this conversion. The detail names which cap (spend.per_tx_usd, spend.daily_usd or spend.velocity_per_hour) and by how much. This is the mandate working rather than an outage: daily_usd refills at UTC midnight and velocity_per_hour within the hour, so back off and POST /v1/intents again later, or PUT /v1/policies to raise the cap if the wallet's mandate genuinely allows it. Also raised when neither leg of the pair is a USD stable, because the caps then have no notional to evaluate against — route through the float token instead.

## FEE_UNFUNDED

The prepaid FEE balance was drained by a concurrent request between this intent's admission and its fee debit, so it was terminally aborted before any execution — nothing was signed, nothing moved, and no fee was charged. This IS the prepaid balance that pays for API calls (the companion of the 402), not the wallet's on-chain funds. POST /v1/balance/topup, then resubmit POST /v1/intents.

## EXECUTION_FAILED

A slice failed on-chain after zero or more recorded fills; everything that settled is ledgered. GET /v1/intents/{id} for the fills that landed, then POST /v1/intents for the remainder if you still want it — this intent is terminal and will not resume.

## EXECUTION_ERROR

An unexpected error stopped execution; recorded fills were persisted before the abort. GET /v1/intents/{id} for what settled before resubmitting, so the remainder is sized against what actually landed rather than against the original request.

## EXECUTION_INTERRUPTED

The run stopped with work left: it ran out of the continuations it is allowed, the policy's execution.deadline_minutes elapsed first, or a broadcast transaction could not be confirmed before the executor had to hand off. NOT an outage and NOT a failed trade — nothing is known to have gone wrong, only to be unresolved, and some of the intent very likely filled. GET /v1/intents/{id} FIRST: the fills that settled are journaled there, and the detail names any transaction hash that was broadcast without a confirmed receipt (that one may still mine after this abort). Size any POST /v1/intents for the remainder against what actually landed; resubmitting the original size double-trades the part that already filled.
