nautechsystems/nautilus_trader · error · anyhow::Error

Cannot quote swap with zero amount

Error message

Cannot quote swap with zero amount

What it means

PoolProfiler::quote_swap simulates a Uniswap-v3-style swap; it rejects a zero amount_specified up front because a zero-amount swap has no defined path or resulting price. The pool must also be initialized (check_if_initialized) before quoting. Callers like swap_exact_in/swap_exact_out and sqrt-price traversal helpers all funnel through this guard.

Source

Thrown at crates/model/src/defi/pool_analysis/profiler.rs:749

    /// - Fee breakdown (LP fees and protocol fees)
    /// - List of crossed ticks with state snapshots
    ///
    /// # Errors
    ///
    /// Returns error if:
    /// - Pool fee is not configured
    /// - Fee growth arithmetic overflows when scaling by liquidity
    /// - Swap step calculations fail
    pub fn quote_swap(
        &self,
        amount_specified: I256,
        zero_for_one: bool,
        sqrt_price_limit_x96: Option<U160>,
    ) -> anyhow::Result<SwapQuote> {
        self.check_if_initialized(PoolEventKind::Swap)?;

        if amount_specified.is_zero() {
            anyhow::bail!("Cannot quote swap with zero amount");
        }

        if let Some(price_limit) = sqrt_price_limit_x96 {
            self.validate_price_limit(price_limit, zero_for_one)?;
        }

        let limit = sqrt_price_limit_x96.unwrap_or_else(|| {
            if zero_for_one {
                MIN_SQRT_RATIO + U160::from(1)
            } else {
                MAX_SQRT_RATIO - U160::from(1)
            }
        });

        self.simulate_swap_through_ticks(amount_specified, zero_for_one, limit, false)
    }

    /// Simulates an exact input swap (know input amount, calculate output amount).

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Skip zero-amount quotes: check amount_specified.is_zero() before calling and return a neutral/default quote instead
  2. Clamp or filter upstream so amounts fed into quote_swap are strictly positive
  3. Ensure the profiler is initialized for the pool before quoting (check_if_initialized passes)
  4. Review the caller producing the amount — a zero usually indicates a missing or mis-decoded input

Example fix

// before
let quote = profiler.quote_swap(amount, zero_for_one, None)?;
// after
if amount.is_zero() {
    return Ok(None); // nothing to quote
}
let quote = profiler.quote_swap(amount, zero_for_one, None)?;
Defensive patterns

Strategy: validation

Validate before calling

if amount_specified.is_zero() {
    return Ok(None); // nothing to quote
}
if !profiler.is_initialized(PoolEventKind::Swap) {
    return Ok(None);
}

Type guard

fn quotable(profiler: &PoolProfiler, amount: U256) -> bool {
    !amount.is_zero() && profiler.is_initialized(PoolEventKind::Swap)
}

Try / catch

match profiler.quote_swap(amount, zero_for_one, limit) {
    Ok(q) => Some(q),
    Err(e) if e.to_string().contains("zero amount") => None,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling quote_swap (directly or via swap_exact_in/swap_exact_out/swap_to_lower_sqrt_price/swap_to_higher_sqrt_price) with amount_specified == 0, or quoting on a pool profiler that was never initialized with a swap/liquidity snapshot.

Common situations: Computers replaying pools with no trade volume passing zero amounts; a loop that processes empty deltas; building quotes from user input where 0 wasn't validated; uninitialized profiler used before mint events loaded.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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