catalogue() holds every code the program can return.
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 raisesProgramRefusal, 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.
Sessions, the pool and Minute
The refusals of a session key are on session keys, anddelegateGates 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.The send you cannot confirm
The send you cannot confirm
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:
- Look the signature up before you give up.
getSignatureStatusessearches only a node’s recent status cache and answersnullfor a signature that landed but has aged out — and if your RPC pool races providers,lastValidBlockHeightcan come from one node andgetBlockHeightfrom another further ahead, expiring your hash early. AskgetTransaction, which finds a signature wherever it is. A hit atconfirmedis definitive. Unknown is the honest answer only when it IS the answer. - 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.
- 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.
/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.RPC failure
RPC failure
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.Composition failure
Composition failure
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 incatalogue(), 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 byopenGates 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.
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.