nautechsystems/nautilus_trader · error

Input token {} balance {} is below the swap amount {}

Error message

Input token {} balance {} is below the swap amount {}

What it means

Before submitting a swap, the client checks the wallet's balance of the input token. This error is thrown when the balance is strictly less than the swap's amount_in, meaning the swap cannot be funded and would revert on-chain.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:5183

    }
    .abi_encode();
    let balance = required_verification(
        executor
            .verification
            .verify_decoded_call(
                None,
                &plan.token_in,
                U256::ZERO,
                &balance_call,
                block,
                |result| ERC20::balanceOfCall::abi_decode_returns(result).map_err(Into::into),
            )
            .await,
        "swap input balance",
    )?;

    if balance.value < plan.amount_in {
        anyhow::bail!(
            "Input token {} balance {} is below the swap amount {}",
            plan.token_in,
            balance.value,
            plan.amount_in
        );
    }
    decisions.push(verification_decision(&balance, Some(block), Some(block)));

    Ok(decisions)
}

/// Converts a base-denominated order quantity into raw token units with exact integer
/// scaling by the base token decimals.
///
/// `Quantity::raw` is scaled to the greater of its declared precision and `FIXED_PRECISION`;
/// token amounts are scaled to the token's decimals. Quantities not exactly representable in
/// token units are rejected rather than rounded.
fn quantity_to_raw_amount(quantity: Quantity, decimals: u8) -> anyhow::Result<U256> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-fetch the on-chain balance and resize the swap to amount_in <= balance
  2. Verify token decimals are correct so amount_in is scaled properly
  3. Transfer or acquire the shortfall of token_in before retrying
  4. Check for other pending transactions consuming the same balance (nonce queue)

Example fix

// before: sizing from stale cached balance
let plan = SwapPlan::new(token_in, cached_balance);
// after: refresh balance and clamp the amount
let balance = token.balance_of(wallet).await?;
let plan = SwapPlan::new(token_in, plan.amount_in.min(balance));
Defensive patterns

Strategy: validation

Validate before calling

let balance = token.balance_of(wallet).await?;
anyhow::ensure!(balance >= plan.amount_in, "insufficient token_in balance: {} < {}", balance, plan.amount_in);

Try / catch

match res {
    Err(e) if e.to_string().contains("balance") => {
        let balance = token.balance_of(wallet).await?;
        submit_swap(plan.resized_to(balance)).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Submitting a swap plan where token_in's balance (queried via the token contract) is below plan.amount_in — e.g. plan sized from stale balance data, tokens transferred out, or amount including fees the wallet cannot cover.

Common situations: Stale balance cache after recent transfers or another trade; token decimals mismatch causing an inflated amount_in; funds locked in another position or pending withdrawal; swap sized from a quote denominated in a different unit.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/54d5b07d9d72fca0. Report an issue: GitHub.