nautechsystems/nautilus_trader · error · anyhow::Error

Position liquidity {} is less than the requested burn amount

Error message

Position liquidity {} is less than the requested burn amount of {}

What it means

When processing a liquidity change, update_position prevents burning more liquidity than the position holds: if liquidity_delta is negative and the requested burn exceeds position.liquidity, it bails with both values in the message. This protects the pool simulation from underflow and preserves accounting invariants (same guard used by add_liquidity, process_burn, execute_burn).

Source

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

        owner: &Address,
        tick_lower: i32,
        tick_upper: i32,
        liquidity_delta: i128,
        amount0: U256,
        amount1: U256,
    ) -> anyhow::Result<()> {
        let current_tick = self.state.current_tick;
        let position_key = PoolPosition::get_position_key(owner, tick_lower, tick_upper);
        let position = self
            .positions
            .entry(position_key)
            .or_insert(PoolPosition::new(*owner, tick_lower, tick_upper, 0));

        // Only validate when burning (negative liquidity_delta)
        if liquidity_delta < 0 {
            let burn_amount = liquidity_delta.unsigned_abs();
            if position.liquidity < burn_amount {
                anyhow::bail!(
                    "Position liquidity {} is less than the requested burn amount of {}",
                    position.liquidity,
                    burn_amount
                );
            }
        }

        // Pre-validate so an over/underflow error returns before mutating tick map
        // or position state.
        let new_active_liquidity = if tick_lower <= current_tick && current_tick < tick_upper {
            Some(try_liquidity_math_add(
                self.tick_map.liquidity,
                liquidity_delta,
            )?)
        } else {
            None
        };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure mints are processed before burns: process events in strict log/block order from pool creation
  2. Clamp the burn to min(position.liquidity, burn_amount) if your replay tolerates rounding discrepancies
  3. Verify the owner and tick_lower/tick_upper used to look up the position match the burn event exactly
  4. Rebuild the profiler state from genesis if position liquidity has diverged

Example fix

// before
profiler.process_burn(owner, tick_lower, tick_upper, amount)?;
// after
let amount = amount.min(position_liquidity(owner, tick_lower, tick_upper));
profiler.process_burn(owner, tick_lower, tick_upper, amount)?;
Defensive patterns

Strategy: validation

Validate before calling

let tracked = position_liquidity(owner, tick_lower, tick_upper);
let burn = burn_amount.min(tracked);
profiler.process_burn(owner, tick_lower, tick_upper, burn)?;

Try / catch

match profiler.process_burn(owner, lower, upper, amount) {
    Err(e) if e.to_string().contains("less than the requested burn amount") => {
        // event ordering/divergence: rebuild state from genesis
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling process_burn/execute_burn (or the shared update path) with a liquidity amount larger than the tracked position's liquidity — e.g. replaying a burn without having processed the mint, wrong owner/tick keys, or double-counted burns.

Common situations: Event streams processed out of order (burn before its mint); burns attributed to the wrong owner or tick range so the tracked position is smaller; re-applying a log range that was already replayed; position maps reset mid-simulation.

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/600845ab2e14b405. Report an issue: GitHub.