SOL-slot-block-tps-priority fee-epoch-mainnet

Errors

Every failure is a named program error. Translate it into a sentence that tells the user what to do next.

Handling

Never hardcode a numeric error code. Anchor assigns them by declaration order, so adding one variant shifts everything after it, and that has already happened twice. Match on the name through parseOtcError(), which returns { code, name, message } or null when the failure was not ours.

TypeScript
import { parseOtcError } from '@otcdex/sdk';

try {
  await sendAndConfirmTransaction(connection, tx, [wallet]);
} catch (err) {
  // Never show the raw simulation log to a user.
  const otc = parseOtcError(err);
  toast.error(otc ? otc.message : 'Transaction failed');
  // Worth re-quoting automatically on a moved price.
  if (otc?.name === 'PriceBoundExceeded' || otc?.name === 'ProceedsBelowMinimum') refresh();
}

Price bound errors are not bugs. They are the protection working: the order was re-priced between your quote and your transaction landing, and the program refused to execute at a worse price than you signed for. Re-quote and resubmit.

Reference

ErrorCodeShow the user
ClampedFillTooLarge6065Too large while the price is catching up to the market. Try a smaller amount.
PoolReservesEmpty6061This token's pool has no liquidity right now.
PoolPriceUnusable6062This token's pool price cannot be read right now.
PoolProgramNotAllowed6058This token's pool is not one we can price against.
PoolLayoutUnexpected6059This token's pool changed shape. Pricing is paused for it.
PoolVaultMismatch6060Wrong pool accounts for this order. Refresh and try again.
PoolMismatch6063This order prices against a different pool. Refresh and try again.
PriceUnchanged6067The price is already up to date.
NotOracleTracked6064This order has a fixed price, so there is nothing to re-anchor.
PricingKindImmutable6066An order cannot switch between fixed and market pricing.
TrackedOrderNotMatchable6068Market-priced orders are filled directly, not matched.
ZeroProceeds6069Too small : the fee would take all of it. Try a larger amount.
FloorRequired6070Set a floor price. Market-tracked orders will not fill below it.
FloorTooWide6071That floor is too far below the market. Raise it to within 25%.
CeilingRequired6072Set a ceiling price. Market-tracked bids will not pay above it.
CeilingTooWide6073That ceiling is too far above the market. Lower it to within 25%.
PriceBoundExceeded6037Price moved. Refresh and try again.
ProceedsBelowMinimum6020The bid dropped below your minimum. Refresh and try again.
FillBelowMinimum6030Below this order's minimum size.
FillExceedsRemaining6032Not enough left : someone bought first.
FillExceedsRemainingQuote6019Not enough left in this bid : someone sold first.
ListingExpired6011This order has expired.
ListingNotActive6010This order is no longer active.
BidNotActive6018This bid is no longer active.
BuyerNotAllowed6015This is a private listing for another wallet.
SellerNotAllowed6016This is a private bid for another wallet.
ProtocolPaused6007Trading is paused.
QuoteMintNotAllowed6039This payment asset is not accepted.
SelfFill6035You cannot fill your own listing.
SelfFillBid6021You cannot fill your own bid.
ZeroTokensReceived6023That amount is too small : the fee would consume all of it.
NotionalCapExceeded6033Order size is above the current cap.
UnsupportedMintExtension6045This token uses a feature the venue does not support.
QuoteMintHasTransferFee6047Payment assets with a transfer fee are not supported.

Anything unrecognised should fall back to a generic message plus the transaction signature. Do not print a simulation log into the interface: it is unreadable to a user and often leaks account addresses they did not ask about. Keep it in console.debug.