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
simulateTransactionresponse carries an error string such asHostError: 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_functionresult such astrappedorresource_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 intoError('Contract execution error: #709 NotFilled')with a numericcodeproperty. An error raised by another contract, normally a token transfer during settlement, becomes anErrorwithtokenError: true, its numericcodeand, when the diagnostic log names it, thecontractthat 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 throwsError('Transaction <hash> failed: <result>')with ahashproperty. 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
Limittrade needs a market and, while the minimum order value is enabled, a fresh cached price, even when it fills completely. UseFillwhen you do not want an order on the book.721and722never affect fills, swaps, crossfills and removals. 708points at the receiving account's trustline,712at 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
crossfillthe 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
skipevent intradeandcrossfill. In aswapthe failed maker transfer fails the whole call with the token's error. trade,updateandcrossfillcheck that the receiving account can hold the asset and fail with708before any transfer.swaphas no such check, so a trader without a trustline for the output gets#13or#11, and one with a full trustline#10.crossfillpays 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).