Error Codes#
Complete reference of Seesaw protocol error codes.
User-Facing Message Matrix#
Apps, SDKs, CLI commands, and API transaction builders should convert raw
program errors into specific recovery messages. Do not surface bare values like
Custom(68) to traders.
| Situation | Program errors or symptoms | User-facing message | Recovery action |
|---|---|---|---|
| Insufficient collateral | InsufficientBalance, transaction simulation fail | Not enough USDT to place this order | Add USDT or reduce order size |
| Insufficient shares | InsufficientBalance, InvalidRedemption | You do not have enough shares for this action | Reduce size or refresh position |
| Trading not started | TradingNotStarted, EpochNotStarted | This market is not live yet | Wait for the start snapshot |
| Trading ended | TradingEnded, EpochNotEnded where applicable | Trading has closed for this market | Claim after resolution or choose another market |
| Already settled/resolved | AlreadyResolved, MarketNotResolved | This market is already resolved or not resolved yet | Refresh market state |
| Orderbook full | OrderbookFull | The order book is full at the moment | Use a matching price or try again later |
| Price outside band | OrderPriceOutOfBand | Maker price is outside the allowed band | Move price closer to the current market |
| Post-only would cross | WouldCross | Post-only order would execute immediately | Adjust price or use a regular limit order |
| Stale oracle | StaleOracle | Waiting for a fresh Pyth price | Retry after the next oracle update |
| Confidence too wide | ConfidenceTooWide | Pyth confidence is too wide for this market | Wait for market conditions to stabilize |
| Oracle not valid/trading | InvalidOracleData, InvalidPrice | Pyth data is not currently usable for this market | Retry or check the feed status |
| Wrong network/account | InvalidAccountOwner, InvalidPDA, wallet error | Wallet or account does not match this deployment | Switch network or reconnect wallet |
| Missing token account | token-account simulation failure | Required token account is missing | Create/fund the token account |
| Transaction timeout | expired blockhash, no confirmation | Transaction was submitted but did not confirm in time | Check signature, then retry if it did not land |
| RPC rate limited | HTTP 429, RPC provider error | RPC provider is rate-limiting requests | Retry with backoff or switch endpoint |
| Wallet rejection | wallet adapter rejection | Transaction was rejected in the wallet | Reopen the action and approve if still intended |
Every failed transaction surface should include the submitted signature when one exists, an explorer link, the current market state if known, and a safe retry instruction.
Overview#
Error codes are organized by category for easy identification:
Market Errors (0x1001 - 0x1011)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x1001 | MarketExists | Market account already exists | Use existing market |
0x1002 | InvalidState | Operation not valid in current market state | Check market state before operation |
0x1003 | TradingNotStarted | Trading has not begun (start price not snapshotted) | Wait for start price snapshot |
0x1004 | TradingEnded | Trading period has ended | Cannot place new orders |
0x1005 | AlreadyResolved | Market outcome already determined | No-op, proceed to settlement |
0x1006 | MarketNotResolved | Market not yet resolved | Wait for resolution |
0x1007 | TooEarly | Operation attempted too early | Wait for appropriate time |
0x1008 | EpochNotStarted | Epoch has not started yet | Wait for t_start |
0x1009 | EpochNotEnded | Epoch has not ended yet | Wait for t_end |
0x100A | NotOwner | Caller is not the owner of the resource | Use correct wallet |
0x100B | PositionsRemaining | Unsettled positions exist; cannot close market | Settle all positions first |
0x100C | MarketCloseTimeout | Market close timeout has not elapsed (resolved_at + 7 days) | Wait for the 7-day timeout |
0x100D | InvalidCreator | Creator account does not match the market's creator | Use the correct creator keypair |
0x100E | VaultNotEmpty | Vault still contains funds; cannot close market | Withdraw all funds first |
0x100F | InvalidDuration | Market duration is outside allowed bounds (60s–604800s) | Use a valid duration |
0x1010 | InvalidConfidenceRatio | Confidence ratio exceeds maximum valid value (10000 bps) | Use a lower confidence ratio |
0x1011 | InvalidStateTransition | State transition is not valid from current state | Check market state before operation |
Market Error Examples#
typescript
// Check if market exists before interacting
try {
const market = await program.account.market.fetch(marketPda);
} catch (e) {
if (e.code === 0x1001) {
console.log('Market already exists');
}
}
// Handle trading state
if (error.code === 0x1004) {
console.log('Trading has ended for this market');
// Redirect to settlement or next market
}
Oracle Errors (0x2001 - 0x2007)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x2001 | OracleMismatch | Pyth feed does not match market configuration | Use correct feed for market |
0x2002 | StaleOracle | Oracle price data is stale | Wait for fresh price update |
0x2003 | InvalidPrice | Oracle reported an invalid price (zero or negative) | Check feed status |
0x2004 | ConfidenceTooWide | Oracle confidence interval exceeds maximum allowed ratio | Wait for more certain price |
0x2005 | FeedIdMismatch | Pyth feed ID does not match expected feed ID stored in market | Verify feed ID |
0x2006 | InvalidOracleData | Oracle price exponent is outside valid range [-18, 18] | Check oracle data format |
0x2007 | OraclePriceJumpTooLarge | Oracle end price moved beyond the circuit-breaker bound | Wait for market to stabilize |
Oracle Error Examples#
typescript
// Handle stale oracle
if (error.code === 0x2002) {
console.log('Oracle price is stale, retrying in 1 second...');
await sleep(1000);
// Retry operation
}
// Handle confidence issues
if (error.code === 0x2004) {
console.log('Price confidence too wide, market may be volatile');
// Consider waiting for better conditions
}
Order Errors (0x3001 - 0x3018)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x3001 | InvalidQuantity | Order quantity is invalid (zero or exceeds maximum) | Use valid quantity (> 0) |
0x3002 | OrderbookFull | Order book has reached maximum capacity | Wait for orders to clear or use IOC |
0x3003 | OrderNotFound | Order not found in the order book | Order may have been filled or cancelled |
0x3004 | WouldCross | PostOnly order would immediately match (cross the spread) | Adjust price or use Limit order |
0x3005 | PriceTooLow | Price is too low (must be at least 1 basis point) | Use price >= 1 bps |
0x3006 | PriceTooHigh | Price is too high (must be less than 10000 basis points) | Use price < 10000 bps |
0x3007 | SlippageExceeded | IOC fill would cross the supplied worst acceptable price | Loosen slippage or improve price |
0x3008 | InsufficientFill | IOC filled fewer units than min_fill_quantity required | Lower min fill or wait for more liquidity |
0x3009 | NoFillForIoc | IOC order matched no resting liquidity | Use Limit/PostOnly or wait for liquidity |
0x300A | SelfTradeAbort | Self-trade encountered while self-trade behavior is Abort | Cancel/modify own resting order first |
0x300B | PostOnlyModeActive | IOC submitted while protocol is in PostOnly mode | Submit Limit or PostOnly |
0x300C | OrderNotExpired | Reclaim target is not expired or flagged expired-claimable | Wait until the order is reclaimable |
0x300D | InsufficientBalance | Taker balance is below required collateral/shares | Fund the relevant token account |
0x300F | MaxTotalSharesExceeded | Mint would exceed market max total shares | Reduce amount or use another market |
0x3010 | AuthorityClaimNotEligible | Authority timelock has not elapsed | Wait until the eligible timestamp |
0x3011 | OrderbookCrossedPostInsert | Orderbook crossed after place-order mutation | Critical invariant violation; report as bug |
0x3012 | InsufficientFreeBalance | Free-funds balance below required amount | Ensure sufficient free-funds balance |
0x3013 | MinRestingNotionalNotMet | Resting order/remainder below configured notional floor | Increase size or use IOC |
0x3014 | OrderPriceOutOfBand | Maker price falls outside the allowed band around mid | Adjust price to within ±1000 bps of mid |
0x3015 | TraderLedgerFull | Trader ledger has reached maximum capacity | Settle open positions to free ledger slots |
0x3016 | OrderbookNotEmpty | Cannot close market; orderbook still has resting orders | Cancel all orders before closing |
0x3017 | TraderLedgerNotAllocated | Trader ledger PDA not yet allocated | Run EnsureTraderLedgerSpace prelude first |
0x3018 | TraderLedgerOversized | Trader ledger account exceeds expected size | Report as bug |
Order Error Examples#
typescript
// Handle insufficient balance
if (error.code === 0x300d) {
const required = calculateCollateral(price, quantity);
console.log(`Need ${required} USDT to place this order`);
// Prompt user to deposit
}
// Handle PostOnly rejection
if (error.code === 0x3004) {
console.log('Your PostOnly order would match immediately');
// Either adjust price or switch to Limit
}
// Handle order not found
if (error.code === 0x3003) {
console.log('Order not found - may have been filled');
// Refresh order book state
}
Math Errors (0x4001)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x4001 | MathOverflow | Arithmetic overflow or underflow | Use smaller values or check inputs |
Math Error Example#
typescript
// This typically indicates a bug or extreme values
if (error.code === 0x4001) {
console.error('Math overflow detected - this should not happen');
console.error('Check your input values:', { price, quantity });
// Report as bug if values are reasonable
}
Account Errors (0x5001 - 0x500E)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x5001 | InvalidAccountType | Account discriminator does not match expected type | Use correct account |
0x5002 | InvalidAccountOwner | Account owner is not the expected program | Verify account ownership |
0x5003 | InvalidPDA | PDA derivation does not match expected seeds | Check PDA derivation |
0x5004 | AlreadyInitialized | Account has already been initialized | Use existing account |
0x5005 | InsolvencyDetected | Vault solvency invariant violated | Critical error — report as bug |
0x5006 | UnauthorizedInitializer | Initializer is not the program's upgrade authority | Use the correct authority |
0x5007 | DuplicateAccount | Two mutable accounts in the same instruction share the same address | Use distinct accounts |
0x5009 | FailedToSerializeEvent | Internal event recorder failed to serialize | Report as bug |
0x500A | FailedToFlushBuffer | Internal event recorder failed to flush buffer | Report as bug |
0x500B | InvalidRecorderPayloadLength | LOG payload length does not match declared count | Report as bug |
0x500C | InvalidRecorderVersion | LOG payload version is not recognized | Update client SDK |
0x500D | InvalidRecorderAuthorityKind | LOG payload authority kind is invalid | Report as bug |
0x500E | InvalidRecorderEventCount | LOG payload event count is out of range | Report as bug |
Account Error Examples#
typescript
// Handle PDA derivation errors
if (error.code === 0x5003) {
// Verify PDA derivation
const [expectedPda] = PublicKey.findProgramAddressSync(
[Buffer.from('seesaw'), Buffer.from('market'), marketIdBuffer],
programId
);
console.log('Expected PDA:', expectedPda.toString());
}
// Handle insolvency (critical)
if (error.code === 0x5005) {
console.error('CRITICAL: Insolvency detected - report as bug immediately');
// This indicates a protocol-level issue
}
Position Errors (0x6001)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x6001 | ProtocolPaused | Protocol is paused | Wait for protocol to resume |
Position Error Example#
typescript
// Handle protocol pause
if (error.code === 0x6001) {
console.log('Protocol is currently paused');
// Check announcements for more info
}
Token Errors (0x7001 - 0x7004)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x7001 | CannotExpire | Cannot expire market (not past expiration window or already resolved) | Wait for expiration window |
0x7002 | InvalidRedemption | Cannot redeem tokens that are not winning | Only winning shares pay out |
0x7003 | InvalidMint | Token account mint does not match expected mint | Use correct token mint |
0x7004 | SolvencyViolation | Solvency invariant violated: vault < max(total_yes, total_no) | Critical — report as bug |
Token Error Examples#
typescript
// Handle losing position redemption attempt
if (error.code === 0x7002) {
console.log('These shares did not win - payout is 0');
}
// Handle market expiration check
if (error.code === 0x7001) {
console.log('Market cannot be expired yet - check expiration window');
}
Fee and Referral Errors (0x8001 - 0x800E)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x8001 | InvalidFeeCurve | Fee curve parameters out of bounds (cap or decay exceeds max) | Use valid fee curve parameters |
0x8002 | InvalidFeeSplit | Fee split shares do not sum to exactly 10,000 | Ensure splits sum to 10000 bps |
0x8003 | ReferrerAlreadySet | Referrer has already been set for this account | Cannot change referrer once set |
0x8004 | SelfReferralForbidden | Account cannot refer itself | Use a different referrer address |
0x8005 | ReferralExpired | Referral relationship has expired | Re-initialize referral relationship |
0x8006 | ReferrerTreasuryMismatch | Referrer treasury PDA does not match expected derivation | Use correct treasury PDA |
0x8007 | AccumulatedAmountZero | Nothing to claim — accumulated amount is zero | Wait for earnings to accumulate |
0x8008 | FeeConfigRateLimit | UpdateFeeConfig called within governance rate-limit window | Wait for rate-limit window to expire |
0x8009 | TreasuryIndexOutOfRange | Treasury recipient index is >= MAX_TREASURY_RECIPIENTS (8) | Use an index in [0, 8) |
0x800A | TreasuryRecipientMismatch | Provided treasury token account does not match stored recipient | Use the correct treasury token account |
0x800B | InvalidTreasuryRecipient | Treasury recipient address is invalid (uninitialized or wrong mint) | Use a valid, funded token account |
0x800C | DuplicateTreasuryRecipient | Two entries in the treasury recipient array share the same address | Use distinct token accounts |
0x800D | TreasuryUpdateRateLimit | UpdateTreasuryRecipients called within rate-limit window | Wait for rate-limit window to expire |
0x800E | OperationalParamsRateLimit | UpdateOperationalParams called within rate-limit window | Wait for rate-limit window to expire |
Fee Error Examples#
typescript
// Handle fee split validation
if (error.code === 0x8002) {
console.log('Fee splits must sum to exactly 10000 bps');
}
// Handle already-set referrer
if (error.code === 0x8003) {
console.log('Referrer is already set and cannot be changed');
}
Authority and Oracle Governance Errors (0x9001 - 0x9005)#
| Code | Name | Description | Resolution |
|---|---|---|---|
0x9001 | NoPendingAuthority | ClaimAuthority called but no authority rotation is pending | Initiate an authority rotation first |
0x9002 | UnauthorizedClaim | Signer does not match the pending authority address | Use the correct pending authority key |
0x9003 | NoPendingOracleUpdate | ApplyPythProgramId called but no oracle program-ID proposal exists | Submit ProposePythProgramId first |
0x9004 | OracleTimelockNotElapsed | ApplyPythProgramId called before the 48-hour timelock expires | Wait until pending_pyth_eligible_at |
0x9005 | InvalidSettlementMint | Proposed default settlement mint fails on-chain validation | Use a valid, initialized SPL mint address |
Error Handling Best Practices#
TypeScript Error Handler#
typescript
function handleSeesawError(error: any): string {
const code = error.code || error.error?.code;
const errorMessages: Record<number, string> = {
0x1001: 'Market already exists',
0x1002: 'Invalid market state for this operation',
0x1004: 'Trading has ended for this market',
0x2002: 'Oracle price is stale - try again shortly',
0x3001: 'Invalid order quantity',
0x300d: 'Insufficient balance for this order',
0x3004: 'PostOnly order would match - adjust price',
// Add more as needed
};
return errorMessages[code] || `Unknown error: ${code}`;
}
Retry Logic#
typescript
async function withRetry<T>(
fn: () => Promise<T>,
options: { maxRetries: number; retryableErrors: number[] }
): Promise<T> {
let lastError: any;
for (let i = 0; i < options.maxRetries; i++) {
try {
return await fn();
} catch (e: any) {
lastError = e;
if (!options.retryableErrors.includes(e.code)) {
throw e;
}
await sleep(1000 * Math.pow(2, i));
}
}
throw lastError;
}
// Use with oracle errors
await withRetry(() => snapshotPrice(market), {
maxRetries: 5,
retryableErrors: [0x2002], // StaleOracle
});
Error Code Quick Reference#
code
MARKET (0x10xx) ORACLE (0x20xx) ORDER (0x30xx)
├─ 0x1001 MarketExists ├─ 0x2001 OracleMismatch ├─ 0x3001 InvalidQuantity
├─ 0x1002 InvalidState ├─ 0x2002 StaleOracle ├─ 0x3002 OrderbookFull
├─ 0x1003 TradingNotStarted ├─ 0x2003 InvalidPrice ├─ 0x3003 OrderNotFound
├─ 0x1004 TradingEnded ├─ 0x2004 ConfidenceTooWide ├─ 0x3004 WouldCross
├─ 0x1005 AlreadyResolved ├─ 0x2005 FeedIdMismatch ├─ 0x3005 PriceTooLow
├─ 0x1006 MarketNotResolved ├─ 0x2006 InvalidOracleData ├─ 0x3006 PriceTooHigh
├─ 0x1007 TooEarly └─ 0x2007 OraclePriceJump ├─ 0x3007 SlippageExceeded
├─ 0x1008 EpochNotStarted ├─ 0x3008 InsufficientFill
├─ 0x1009 EpochNotEnded MATH (0x40xx) ├─ 0x3009 NoFillForIoc
├─ 0x100A NotOwner └─ 0x4001 MathOverflow ├─ 0x300A SelfTradeAbort
├─ 0x100B PositionsRemaining ├─ 0x300B PostOnlyModeActive
├─ 0x100C MarketCloseTimeout ACCOUNT (0x50xx) ├─ 0x300C OrderNotExpired
├─ 0x100D InvalidCreator ├─ 0x5001 InvalidAccountType ├─ 0x300D InsufficientBalance
├─ 0x100E VaultNotEmpty ├─ 0x5002 InvalidAccountOwner├─ 0x300F MaxTotalSharesExceeded
├─ 0x100F InvalidDuration ├─ 0x5003 InvalidPDA ├─ 0x3010 AuthorityClaimNotEligible
├─ 0x1010 InvalidConfidenceRatio├─ 0x5004 AlreadyInitialized├─ 0x3011 OrderbookCrossedPostInsert
└─ 0x1011 InvalidStateTransition├─ 0x5005 InsolvencyDetected├─ 0x3012 InsufficientFreeBalance
├─ 0x5006 UnauthorizedInitializer├─ 0x3013 MinRestingNotionalNotMet
POSITION (0x60xx) ├─ 0x5007 DuplicateAccount ├─ 0x3014 OrderPriceOutOfBand
└─ 0x6001 ProtocolPaused ├─ 0x5009 FailedToSerializeEvent├─ 0x3015 TraderLedgerFull
├─ 0x500A FailedToFlushBuffer├─ 0x3016 OrderbookNotEmpty
TOKEN (0x70xx) ├─ 0x500B InvalidRecorderPayloadLength├─ 0x3017 TraderLedgerNotAllocated
├─ 0x7001 CannotExpire ├─ 0x500C InvalidRecorderVersion└─ 0x3018 TraderLedgerOversized
├─ 0x7002 InvalidRedemption ├─ 0x500D InvalidRecorderAuthorityKind
├─ 0x7003 InvalidMint └─ 0x500E InvalidRecorderEventCount
└─ 0x7004 SolvencyViolation
FEE/REFERRAL/TREASURY (0x80xx) AUTHORITY/GOVERNANCE (0x90xx)
├─ 0x8001 InvalidFeeCurve ├─ 0x9001 NoPendingAuthority
├─ 0x8002 InvalidFeeSplit ├─ 0x9002 UnauthorizedClaim
├─ 0x8003 ReferrerAlreadySet ├─ 0x9003 NoPendingOracleUpdate
├─ 0x8004 SelfReferralForbidden ├─ 0x9004 OracleTimelockNotElapsed
├─ 0x8005 ReferralExpired └─ 0x9005 InvalidSettlementMint
├─ 0x8006 ReferrerTreasuryMismatch
├─ 0x8007 AccumulatedAmountZero
├─ 0x8008 FeeConfigRateLimit
├─ 0x8009 TreasuryIndexOutOfRange
├─ 0x800A TreasuryRecipientMismatch
├─ 0x800B InvalidTreasuryRecipient
├─ 0x800C DuplicateTreasuryRecipient
├─ 0x800D TreasuryUpdateRateLimit
└─ 0x800E OperationalParamsRateLimit