AXIS Docs

DevelopersContract reference

Errors

AXIS contract error codes, what raises them and how to fix them, plus the token and host errors that can fail a call.

How errors surface#

A failed AXIS call keeps nothing of what it did: no transfer, no order change, no approval and no event.

  • Simulation. Most failures show up when the transaction is simulated, before anything is signed. The RPC simulateTransaction response carries an error string such as HostError: Error(Contract, #709), followed by a diagnostic event log. Nothing is submitted and nothing is paid.
  • On-chain. A transaction that passed simulation can still fail when the ledger changed before it was applied, for example a maker spent their balance. The transaction fails as a whole with an invoke_host_function result such as trapped or resource_limit_exceeded, and its fee is charged.
  • JS client. AxisContractClient (and every high-level class built on it) turns an AXIS code found in a simulation error into Error('Contract execution error: #709 NotFilled') with a numeric code property. An error raised by another contract, normally a token transfer during settlement, becomes an Error with tokenError: true, its numeric code and, when the diagnostic log names it, the contract that raised it, and its message says that either side of the transfer may be at fault. Any other simulation error, including host errors, is rethrown unchanged as the RPC's error string. A transaction that fails on-chain throws Error('Transaction <hash> failed: <result>') with a hash property. See Contract client.

ContractErrors maps every code to its name:

import {ContractErrors} from '@axis-markets/client'

console.log(ContractErrors[722].message) // 'AssetPriceOracleFetchFailed'

// account, XLM and USDC as in the Getting started example
try {
    await account.sell({selling: XLM, buying: USDC, amount: 1_000_000_000n, price: 250_000_000_000_000_000n})
} catch (e) {
    const known = ContractErrors[e?.code]
    if (e?.tokenError) {
        console.log(`Token ${e.contract} failed a transfer with #${e.code}: check the balance and allowance, then the makers' trustlines`)
    } else if (e?.code === 722) {
        console.log('No fresh oracle price for this market: requote it, then retry')
    } else if (known) {
        console.log(`AXIS error ${e.code} ${known.message}`)
    } else {
        throw e
    }
}

Contract errors#

Codes come from errors.rs. Each function on the Functions page lists the codes it raises.

Code Error Name Meaning Raised by Typical fix
701 NotAuthorized The caller does not own an order listed in update, removals included. update Send the update from the order's owner. Check owner with the order view.
702 InsufficientBalance The balance does not cover the new order: the remainder of a Limit trade, or the sum of the updated orders selling one token. trade, update Lower the amount or fund the account.
703 InsufficientAllowance The allowance granted to the contract does not cover the new order. trade, update Pass an Approval with the call. It is absolute, so cover every open order selling that token.
704 InvalidMatch selling equals buying in trade or subsidize, or swap got an empty path or a non-positive amount. trade, swap, subsidize Use two different assets. Give swap at least one step and positive amounts.
705 InvalidPrice The price is outside 1 to 10^36. trade, update Scale prices by 10^18 (see units).
706 InvalidAmount An amount is not positive (trade, subsidize) or negative (update, set_floor, constructor), subsidize would open a market with less than the listing fee, or a configuration value is out of range (set_listing_min_days above 255, set_ledger_time of 0 or above 20). trade, update, subsidize, set_floor, set_listing_min_days, set_ledger_time, constructor Use a positive amount. Pay at least market_listing_fee to open a market. Keep configuration values within their ranges.
707 InvalidExpiration expires is neither 0 nor in the future. trade (Limit), update Use 0 or a future UNIX timestamp in seconds.
708 CannotReceive The account that must receive an asset cannot: its trustline is missing or deauthorized. In a trade, also when the final forward of the bought asset to the trader fails, for example on a full trustline. trade, update, crossfill Add or authorize the trustline before trading, and keep room under its limit.
709 NotFilled A FillOrKill trade or a swap cannot execute within its bounds. trade (FillOrKill), swap Get a fresh quote, list more orders or allow more slippage.
710 OrderNotFound The crossfill taker order does not exist or has expired. crossfill Refresh your order list. The order was filled, removed or expired.
711 OrderExists A live order of the trader already uses the remainder's ID (nonce reuse). trade (Limit) Use another nonce. The JS client generates a new one on every call.
712 IntermediaryCannotReceive The AXIS contract cannot hold an asset it passes on: the asset's issuer requires authorization and has not authorized the contract. trade, crossfill, swap Nothing the trader can fix. The issuer has to call set_authorized(<AXIS contract>, true) on the asset contract, see Assets that require authorization. Selling the asset keeps working.
720 OrderSizeTooSmall The order is worth less than min_trade_size at the cached oracle price, or it is dust (worth less than one base unit of the counter asset). trade (Limit), update Increase the amount. The Testnet minimum is 0.001 USD.
721 AssetsNotVerifiedByOracle The pair has no market, or neither asset is quoted by the oracle. trade (Limit), update (changing an order), subsidize Open the market with subsidize, or trade with Fill, FillOrKill or swap, which need no market. Orders on such a market can still be removed.
722 AssetPriceOracleFetchFailed No usable cached price: none was cached with the current oracle's decimals, or it is older than 72 hours. trade (Limit), update Call requote for the market (anyone can). If the oracle feed access lapsed, call subsidize first.
723 InvalidOracleConfig The oracle has more than 24 price decimals or no positive daily fee, or the listing fee overflows. constructor, set_oracle, set_listing_min_days Safety admin only: use a compatible oracle.
730 Frozen The safety admin froze the contract. trade, swap, crossfill, subsidize, requote, update Wait for the unfreeze. Removals and approvals through update still work.
740 Overflow An arithmetic invariant is violated: a price calculation does not fit i128, an operand is negative or a divisor is zero, or a crossfill surplus would be negative. trade, swap, crossfill, update Use realistic amounts and prices.

Notes on frequent cases:

  • A Limit trade needs a market and, while the minimum order value is enabled, a fresh cached price, even when it fills completely. Use Fill when you do not want an order on the book. 721 and 722 never affect fills, swaps, crossfills and removals.
  • 708 points at the receiving account's trustline, 712 at the issuer's authorization of the contract itself. Failure modes sorts every failure by who is at fault.
  • Missing or wrong signatures are not contract errors. See host errors.

Token errors#

AXIS moves tokens with transfer_from and transfer on the token contracts. When a token call fails, the token's own error ends the call. For a Stellar Asset Contract it looks like an AXIS code but with a small number, for example HostError: Error(Contract, #10). The JS client reports it as an Error with tokenError: true, the token's code and the token contract (see How errors surface).

Code SAC error When it shows up in AXIS calls
#9 AllowanceError The payer's allowance does not cover the payment to a maker: the trader's, or in crossfill the taker order owner's. An Approval with live_until beyond the network maximum, or in the past with a positive amount.
#10 BalanceError The payer's balance does not cover the payment to a maker. A maker's trustline for the asset they receive has no room left for the payment. A full trustline of the swap trader, or of the crossfill owner or caller, for their payout. The sponsor of subsidize lacks XRF. A maker's balance is locked (for example by reserves or classic liabilities) in a strict swap.
#11 BalanceDeauthorizedError The issuer deauthorized the trustline of an account that pays or receives.
#13 TrustlineMissingError A classic account has no trustline for the asset, for example a swap trader who cannot receive the output, or a sponsor without an XRF trustline.

Where AXIS checks first and where it does not:

  • A failed payment to a maker fails the whole call with the token's error, nothing moves and no maker is flagged. The error cannot tell a payer who cannot pay from a maker who cannot be credited although authorized (a full trustline), so check your own balance and allowance first, then leave out the maker whose line has no room and retry. In crossfill the payer is the taker order's owner, so this failure is never the caller's.
  • A maker whose asset cannot be collected is skipped with a skip event in trade and crossfill. In a swap the failed maker transfer fails the whole call with the token's error.
  • trade, update and crossfill check that the receiving account can hold the asset and fail with 708 before any transfer. swap has no such check, so a trader without a trustline for the output gets #13 or #11, and one with a full trustline #10. crossfill pays the taker order's owner and the caller with plain transfers, so a full trustline on either side fails the call with the token's error.

Custom SEP-41 tokens report their own error codes, and a token that lacks a function AXIS calls fails the call. See Custom tokens and Failure modes.

Host errors#

These come from the Soroban host, not from AXIS or a token:

Error Cause What to do
Error(Auth, InvalidAction) A required signature is missing or wrong: the trader did not sign the call or its approve sub-invocation, the sponsor did not sign the oracle track and XRF burn, or a non-admin called a safety admin function. Sign with the right account. When the trader is the transaction source, one envelope signature covers the whole call.
Error(WasmVm, InvalidAction) The contract trapped. The release build uses overflow-checks, so plain integer overflow traps instead of returning 740. Use realistic amounts.
Error(Budget, ExceededLimit) The call ran out of CPU or memory budget, for example too many listed orders or a token contract that burns the budget. List fewer orders per call. See Resource limits and costs.

Two more failures are not contract errors at all:

  • Archived entries. An order, market or token entry that was not touched for a long time is archived. Simulation marks the archived entries a call touches for restore, and the transaction restores them and pays their rent. An archived entry declared without that mark, such as a listed spare order the simulation did not reach, fails the transaction when it is applied. See Archival and restore.
  • Footprint and resource limits. A transaction declares the ledger entries and resources it may use. If the book changes between simulation and execution and the call needs an entry it did not declare, the transaction fails. The JS client declares the entries of every listed order and maker to avoid this (see footprint completion).