nautechsystems/nautilus_trader · error · anyhow::Error

Price limit must be less than current price for zero_for_one

Error message

Price limit must be less than current price for zero_for_one swaps

What it means

When quoting a zero_for_one swap (token0 -> token1), the pool price must move DOWN, so the caller-supplied sqrt price limit must be strictly below the current sqrt price. validate_price_limit bails if limit_price_sqrt >= current price_sqrt_ratio_x96, since such a limit would never be crossed and the swap simulation would be invalid.

Source

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

        zero_for_one: bool,
    ) -> anyhow::Result<size_estimator::SizeForImpactResult> {
        let config = size_estimator::EstimationConfig::default();
        size_estimator::size_for_impact_bps_detailed(self, impact_bps, zero_for_one, &config)
    }

    /// Validates that the price limit is in the correct direction for the swap.
    ///
    /// # Errors
    /// Returns error if price limit violates swap direction constraints.
    fn validate_price_limit(
        &self,
        limit_price_sqrt: U160,
        zero_for_one: bool,
    ) -> anyhow::Result<()> {
        if zero_for_one {
            // Swapping token0 for token1: price must decrease
            if limit_price_sqrt >= self.state.price_sqrt_ratio_x96 {
                anyhow::bail!("Price limit must be less than current price for zero_for_one swaps");
            }
        } else {
            // Swapping token1 for token0: price must increase
            if limit_price_sqrt <= self.state.price_sqrt_ratio_x96 {
                anyhow::bail!(
                    "Price limit must be greater than current price for one_for_zero swaps"
                );
            }
        }

        Ok(())
    }

    /// Processes a mint (liquidity add) event from historical data.
    ///
    /// Updates pool state when liquidity is added to a position, validates ticks,
    /// and delegates to internal liquidity management methods.
    ///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass the correct sentinel: for zero_for_one use the minimum sqrt price limit (e.g. MIN_SQRT_RATIO), for one_for_zero the maximum
  2. Compute the limit relative to the current price: ensure limit < state.price_sqrt_ratio_x96 for zero_for_one
  3. Check the zero_for_one flag is not inverted at the call site
  4. Pass None for sqrt_price_limit_x96 if no specific bound is required

Example fix

// before
let quote = profiler.quote_swap(amount, true, Some(current_sqrt_price))?;
// after
let limit = MIN_SQRT_PRICE_X96; // must be below current price for zero_for_one
let quote = profiler.quote_swap(amount, true, Some(limit))?;
Defensive patterns

Strategy: validation

Validate before calling

if zero_for_one {
    anyhow::ensure!(limit < profiler.current_sqrt_price(), "limit must be below current price");
}

Type guard

fn valid_zero_for_one_limit(limit: U160, current: U160) -> bool { limit < current }

Try / catch

match profiler.quote_swap(amount, true, Some(limit)) {
    Ok(q) => q,
    Err(e) if e.to_string().contains("must be less than current price") => {
        profiler.quote_swap(amount, true, Some(MIN_SQRT_PRICE_X96))?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling quote_swap with sqrt_price_limit_x96 = Some(limit) where zero_for_one is true and limit >= current sqrt price — commonly passing MIN/MAX sentinel constants on the wrong side, or reusing a limit computed for the opposite direction.

Common situations: Using sqrt price limit constants meant for one_for_zero swaps in zero_for_one direction; passing the current price as the limit (no movement allowed); direction flags inverted when translating from amount-in semantics.

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/9d57582512c7c25a. Report an issue: GitHub.