Skip to main content
The error catalogue is generated from the SDK’s client IDL, so it cannot drift from what the chain actually raises. What is hand-written is the part a name cannot carry: what caused it, what to do instead, and whether retrying is useful or an infinite loop. This page lists the refusals a client meets. catalogue() holds every code the program can return.
An agent that retries RungBelowFloor loops forever. One that gives up on NoFreshPrice fails a trade that would have landed on the next slot. Branch on retryable, never on the name.

The four retry classes

Switch on all four. A client that folds after-governance into a default branch retries a venue-wide stop every few seconds.
The codes of orders and closes (6203 to 6238) have a finer class. retryClassOf(code) returns one of nine classes for every code the program can raise, and RETRY_CLASS_LABEL has the words: for example “retry after the state changes”, “retry with the right quote”, “send the other instruction” and “done”. contextOf(code) adds the note for a code whose action depends on what was sent. Bind to the class, not to a remembered number. The table a client meets is under orders, closes and stamps.

Resolving an error

Catching a refusal

Every write path in the SDK raises ProgramRefusal, which carries the whole catalogue entry rather than a string — because an interface has to render the fix next to the failure, and a caller has to branch on retryable without parsing prose.

The refusals a client actually meets

Everything below is a real entry in the catalogue. Names, codes and messages come from the client IDL.

Orders, closes and stamps

An open and an add are orders, and a close is a request. The rules are on orders and closes. These are the refusals a client meets on that path. A returned order (OpenVoided) and a returned add (AddReversed) are not refusals. They are normal outcomes, and the stake comes back (refundTotal).

Opening

An open carries no quote, so no row below can judge one: the fill judges the first price, and a price that fails returns the order whole. A market with trading hours — see markets — refuses an open or an add when its session is closed (MarketClosed) or too close to its end (SessionEndsBeforeTenor). Both clear on their own, need no governance, and are not a mistake in the request. availability() says when the market opens.

Settling

While the oracle is frozen, a lastResort close is refused OracleFrozen (6190) and every priced close fails NoFreshPrice (6007). If NoFreshPrice repeats on a settle, read config.oracleFrozen before you retry again.

Payout destination

Adding to a position

add_collateral is an open in every respect but one — it grows a slot instead of creating one — so every refusal in the Opening table above can also refuse an add, including the guardian freeze, the pauses and the event window. These are its own. An add carries no quote: the freshness gate does not apply, and an add whose price is at or through a level is returned (AddReversed) rather than refused.
CollateralOutOfRange (6049) answers three different facts on an add, and only one of them is worth retyping. The minimum binds on the increment; the maximum binds on the total. So the code means the increment was under the floor, or the new total would be over the ceiling, or — the case that traps a user — the position already holds more than maxCollateral − minCollateral, so no legal increment exists at all and every value the trader types will be refused.A client must compute the admissible interval and render it, rather than showing one code and a text box. For example, with a minimum stake of 0.20andamaximumof0.20 and a maximum of 1,000, a position holding more than $999.80 can take no add.

Sessions, the pool and Minute

The refusals of a session key are on session keys, and delegateGates predicts them. A session usually ends at the token program with InsufficientFunds when its allowance is spent, in none of the shapes on this page: sessionAllowanceGates is the only thing that predicts it, and renewSession is the only call that resets all three clocks — expiry, window and allowance — together. The refusals of an LP’s redemption are on providing liquidity, and MinuteDisabled is on integrating Minute. catalogue() holds every other code the program can return, each with its cause, its fix and its retry class.

Errors that are not the program’s

Three failure classes reach you that have nothing to do with the protocol. Conflating them sends people to debug the wrong thing, and the first one costs the most.
You broadcast a transaction and could not determine its outcome. This is not a refusal and it is not a failure. It is the absence of an answer, and it is the class every client gets wrong first.A blockhash expiring bounds how long a transaction can be ACCEPTED for. It says nothing about whether one already was. So “the blockhash expired, therefore nothing happened” is not an inference — it is a guess.Three rules:
  1. Look the signature up before you give up. getSignatureStatuses searches only a node’s recent status cache and answers null for a signature that landed but has aged out — and if your RPC pool races providers, lastValidBlockHeight can come from one node and getBlockHeight from another further ahead, expiring your hash early. Ask getTransaction, which finds a signature wherever it is. A hit at confirmed is definitive. Unknown is the honest answer only when it IS the answer.
  2. Say “unknown”, never “nothing happened”. A client may say refused, it may say unknown, and it may say nothing moved only with a decoded program error code in hand. Carry the signature in the message so the trader can go and look.
  3. Keep local state on an unknown. Delete a session secret, clear a pending record or free a nonce only on a decoded refusal. The branch where it landed is the expensive one — see sessions.
And decode before you pattern-match the text. A client that tests the error message for /reject|denied|cancel/i before asking for the program error code will misread the program’s own words: 6042 RedemptionExpired carries cancel in its message — “the ticket’s execution window has closed; cancel it and request again” — and 6093 ArmedTicketNotCancellable carries it in its name. A landed, fee-paying revert read that way comes back to a trader as “you declined it in your wallet”, which is false. Ask for the code first. A wallet decline can never carry one, because the wallet refuses before a transaction exists.
AllProvidersFailed when every configured endpoint failed. With readCached you get the last good value marked stale instead — render it with its age rather than showing a spinner that never resolves.
SenderRefusal from the sender: the transaction could not be built or bound correctly. This is a bug in the caller or a vendor payload that does not match what the quote program expects. It is not a threshold and retrying does not help.

The codes of the liquidity pool

The liquidity pool has refusals of its own for the cover rule, the queue and the wind-down. They are in catalogue(), each with its retry class. At a fill the cover rule never refuses: an order that does not fit is returned with the whole stake and no fee, so show “try again when the pool has room”. A transaction that lacks a capital account is a build error, and the SDK adds those accounts for you.

Predict, do not discover

The best error handling is the error you never raise. Most of the Opening table above is predicted by openGates before you spend a fee, with the value it measured and the threshold it was judged against, and most of the Settling table by settleGates and settlePreview. Neither report reads the guardian flags: read config.oracleFrozen and config.settlesDisabled yourself.
MinimumHoldNotMet (6103) is in the Settling table and neither report predicts it. The gate’s only input is the position’s opened slot, and neither settleGates nor settlePreview reads that field. settleGates emits six rows — market pin, fresh price, exponent, reason legality, outflow against reserve, liquidator token — and none of them is the hold; settlePreview’s voluntary and takeProfit legality is the signer and the price, with no slot term in it. A client that treats green gates as “the chain accepts it” meets 6103 on chain. An add re-stamps the position’s opened slot with the current slot, so a close within the hold of an add meets it as well as one within the hold of the open.
The Adding table’s shared refusals are predicted by addGates, the report for an add. Use the catalogue for the cases you could not predict. Use the gates for everything else.