Error Codes
Parlay program
Section titled “Parlay program”Anchor error codes from high-market-parlay, in declaration order (Anchor
assigns 6000 + index).
| Code | Name | When |
|---|---|---|
| 6000 | Paused | create_parlay while config.is_paused == true |
| 6001 | Unauthorized | Signer isn’t the admin, an account ownership check failed, or a void_parlay user mismatch |
| 6002 | TooFewLegs | legs.len() < 2 |
| 6003 | TooManyLegs | legs.len() > config.max_legs |
| 6004 | DuplicateMarket | Two legs reference the same market_id |
| 6005 | SameEventCorrelation | Two legs reference markets sharing an event_id |
| 6006 | InvalidEventLink | A MarketEventLink doesn’t match the derived PDA, has a wrong discriminator, or its embedded market_id differs |
| 6007 | RemainingAccountsLengthMismatch | Not exactly 2 × legs.len() remaining accounts |
| 6008 | StakeTooLow | stake < config.min_stake |
| 6009 | StakeTooHigh | stake > config.max_stake |
| 6010 | PayoutExceedsMax | potential_payout > config.max_payout |
| 6011 | ExposureLimitExceeded | total_exposure + payout > 0.8 × (vault.amount + stake) |
| 6012 | MarketNotOpen | A leg’s market isn’t Open at create time |
| 6013 | MarketNotResolved | settle_leg before the market is Resolved or Cancelled |
| 6014 | ProbabilityOutOfRange | A leg falls outside the configured [prob_floor_bps, prob_ceiling_bps] band |
| 6015 | MarketTooNew | Market younger than config.min_market_age_secs |
| 6016 | MarketTooShallow | AMM liquidity b below config.min_market_b |
| 6017 | MarketTooIlliquid | total_volume below config.min_market_volume |
| 6018 | InvalidProbabilityBand | Admin band violates HARD_PROB_FLOOR ≤ floor < ceiling ≤ HARD_PROB_CEILING |
| 6019 | InvalidMarketAge | min_market_age_secs < TWAP_WINDOW_SECS |
| 6020 | InvalidAmmProgramId | initialize called with anything but the compiled-in EXPECTED_AMM_PROGRAM_ID |
| 6021 | MarketExpired | A leg’s market is past its end_time |
| 6022 | FreezableCollateralMint | The collateral mint retains a freeze authority (Circle USDC is the one exception) |
| 6023 | CollateralMintMismatch | A leg settles in a different collateral mint than the parlay vault |
| 6024 | ParlayHasResolvedLegs | void_parlay on an Active slip that already has a resolved leg |
| 6025 | NoPendingAdmin | accept_admin with nothing queued |
| 6026 | ClaimWindowNotElapsed | void_parlay on a Won slip still inside its claim window |
| 6027 | ParlayNotVoidable | void_parlay on an already-terminal slip |
| 6028 | InvalidClaimDeadline | claim_deadline_secs outside [1 hour, ~3 years] |
| 6029 | UseUpdateTreasuryInstruction | Treasury passed to update_config instead of update_treasury |
| 6030 | InvalidAdminPubkey | Admin set to the all-zeros / system program address |
| 6031 | ParlayNotWon | claim_parlay on a slip that is neither Won nor Voided |
| 6032 | ParlayNotActive | settle_leg on a non-Active slip |
| 6033 | LegNotPending | settle_leg on an already-terminal leg |
| 6034 | InvalidLegIndex | leg_index >= parlay.legs.len() |
| 6035 | MarketAddressMismatch | The market account doesn’t match leg.market_address |
| 6036 | InvalidMarketOwner | A market or link account isn’t owned by config.amm_program_id |
| 6037 | InvalidMarketAccount | Discriminator or layout check failed deserialising as Market |
| 6038 | InsufficientVaultFunds | claim_parlay or withdraw_vault would over-draw |
| 6039 | MathOverflow | Checked arithmetic overflowed |
| 6040 | LmsrPriceFailed | LMSR price_yes returned None |
| 6041 | CombinedProbabilityZero | The combined probability truncated to zero |
| 6042 | SlippageExceeded | potential_payout < min_payout |
| 6043 | InvalidAmount | seed_vault / propose_withdraw_vault with amount == 0 |
| 6044 | InvalidFee | fee_bps > MAX_FEE_BPS (1000 = 10%) |
| 6045 | InvalidMaxLegs | max_legs outside [2, 10] |
| 6046 | InvalidStakeBounds | min_stake == 0 or min_stake > max_stake |
| 6047 | InvalidMaxPayout | max_payout == 0 |
| 6048 | TwapWindowTooShort | Not a full averaging window of price history |
| 6049 | TwapTooStale | The market hasn’t traded recently enough to price |
| 6050 | TimelockNotElapsed | accept_admin or withdraw_vault before the 48-hour delay |
| 6051 | NoPendingWithdrawal | withdraw_vault / cancel_withdraw_vault with nothing queued |
| 6052 | WithdrawalMismatch | withdraw_vault amount or destination differs from what was proposed |
By source
Section titled “By source”| Source | Codes |
|---|---|
| Slip construction | 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6042 |
| Market quality | 6012, 6014, 6015, 6016, 6017, 6021, 6023, 6048, 6049 |
| Cross-program account checks | 6006, 6020, 6036, 6037 |
| Vault solvency | 6010, 6011, 6038 |
| State-machine guards | 6013, 6024, 6026, 6027, 6031, 6032, 6033, 6034, 6035 |
| Admin config writes | 6018, 6019, 6022, 6028, 6029, 6030, 6044, 6045, 6046, 6047 |
| Timelocks | 6025, 6050, 6051, 6052 |
| Genuine overflow | 6039, 6040, 6041 |
| Authorisation | 6001 |
Which are retryable
Section titled “Which are retryable”| Behaviour | Codes |
|---|---|
| Harmless — already done | 6033 (LegNotPending) |
| Retry with different input | 6008, 6009, 6010, 6011, 6042 — adjust stake or min_payout |
| Wait | 6013 (market not terminal), 6050 (timelock running) |
| Don’t retry — the slip is invalid | 6002, 6003, 6004, 6005, 6006, 6012, 6014, 6015, 6016, 6017, 6021, 6023 |
| Escalate | 6020, 6036, 6037, 6039, 6040, 6041 — a bug or a bad account |
The user_seq collision is not an Anchor error — it surfaces as Solana’s
“account already in use,” because the Parlay PDA is seeded on
(user, user_seq). Increment user_seq and retry; the webapp retries up to
four times.
User-facing messages
Section titled “User-facing messages”What the webapp shows:
| Error | Message |
|---|---|
ExposureLimitExceeded | Vault exposure limit exceeded — reduce stake |
PayoutExceedsMax | Potential payout exceeds the maximum |
StakeTooLow / StakeTooHigh | Stake below/above allowed range |
MarketExpired | A selected market has expired |
MarketTooNew | A market is too new for parlays |
MarketTooShallow / MarketTooIlliquid | A market has too little liquidity |
TwapTooStale / TwapWindowTooShort | A market hasn’t traded recently enough to price |
ProbabilityOutOfRange | A selected outcome’s odds are outside the allowed band |
SameEventCorrelation | Two legs are from the same event |
DuplicateMarket | Same market added twice |
CollateralMintMismatch | A market uses an unsupported collateral token |
CLOB rejections
Section titled “CLOB rejections”The order manager returns success: false with a human-readable errorMsg
rather than numeric codes.
Insert
Section titled “Insert”| Reason | Cause | Fix |
|---|---|---|
| Invalid signature | Order-hash layout mismatch | Check field order and LE encoding byte for byte |
| Unsupported signature type | signatureType != 0 | Only EOA settles on-chain |
| Signer mismatch | signer != maker | They must be the same key |
| Non-public taker | taker isn’t the zero address | Bilateral orders aren’t supported |
| Invalid nonce | nonce != "0" | There is no on-chain nonce account |
| Tick-size violation | Price off the grid | Round the amounts so the ratio lands on a tick |
| Fee rate too low | A marketable order below the taker fee | Set the taker rate |
| Insufficient balance | ATA short, or reserved by earlier orders | Fund or cancel |
| Book not found | Market not in the catalogue, or not ready | Check GET /v1/markets |
| Duplicate order | The hash already exists | Change the salt |
| Market not ready | is_market_ready failed | Wait |
| Expired | expiration in the past | — |
A missing delegate allowance is a warning, not a rejection. The order rests and settlement fails on-chain. Set it first.
Cancel
Section titled “Cancel”not_canceled maps each order hash to a reason — already filled, already
cancelled, or not owned by you. Cancels aren’t atomic across a batch; always
read both fields.
Trade status
Section titled “Trade status”| Status | Meaning |
|---|---|
MINED | The transaction landed, not yet final |
CONFIRMED | Finalized on Solana — the settlement point |
FAILED | Settlement failed; affected orders reconciled |
On FAILED the executor invalidates the resting orders involved via the
internal OrderSizeUpdate route. Your order disappears and the fill never
happened.
Executor
Section titled “Executor”| Condition | Behaviour |
|---|---|
DuplicateTrade | The trade key was already seen — rejected, not retried |
| Out of gas (CU ≥ 95% of limit) | Retried with CU ×2, fee ×2, up to RETRIES_LIMIT |
Stuck past STUCK_TX_MAX_IDLE_TIME | Re-executed through the same path |
| Final failure | UpdateTrade(FAILED) + order reconciliation |
Platform API
Section titled “Platform API”Standard DRF conventions.
| Status | Meaning |
|---|---|
400 | Validation failure; body carries field errors |
401 | Missing or invalid JWT |
403 | Authenticated but not permitted (superuser routes, webhook secret mismatch) |
404 | Not found — or not yours; the two are indistinguishable by design |
502 | Upstream RPC failure (e.g. vault/sync/) |
503 | A required secret isn’t configured (webhook routes) |
The 404-for-unauthorised choice is deliberate: parlay ids must not be
probeable. Don’t interpret it as “deleted.”