{"record":{"id":"600845ab2e14b405","repo":"nautechsystems/nautilus_trader","slug":"position-liquidity-is-less-than-the-requested-b","errorCode":null,"errorMessage":"Position liquidity {} is less than the requested burn amount of {}","messagePattern":"Position liquidity (.+?) is less than the requested burn amount of (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/model/src/defi/pool_analysis/profiler.rs","lineNumber":1494,"sourceCode":"        owner: &Address,\n        tick_lower: i32,\n        tick_upper: i32,\n        liquidity_delta: i128,\n        amount0: U256,\n        amount1: U256,\n    ) -> anyhow::Result<()> {\n        let current_tick = self.state.current_tick;\n        let position_key = PoolPosition::get_position_key(owner, tick_lower, tick_upper);\n        let position = self\n            .positions\n            .entry(position_key)\n            .or_insert(PoolPosition::new(*owner, tick_lower, tick_upper, 0));\n\n        // Only validate when burning (negative liquidity_delta)\n        if liquidity_delta < 0 {\n            let burn_amount = liquidity_delta.unsigned_abs();\n            if position.liquidity < burn_amount {\n                anyhow::bail!(\n                    \"Position liquidity {} is less than the requested burn amount of {}\",\n                    position.liquidity,\n                    burn_amount\n                );\n            }\n        }\n\n        // Pre-validate so an over/underflow error returns before mutating tick map\n        // or position state.\n        let new_active_liquidity = if tick_lower <= current_tick && current_tick < tick_upper {\n            Some(try_liquidity_math_add(\n                self.tick_map.liquidity,\n                liquidity_delta,\n            )?)\n        } else {\n            None\n        };\n","sourceCodeStart":1476,"sourceCodeEnd":1512,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/defi/pool_analysis/profiler.rs#L1476-L1512","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure mints are processed before burns: process events in strict log/block order from pool creation","Clamp the burn to min(position.liquidity, burn_amount) if your replay tolerates rounding discrepancies","Verify the owner and tick_lower/tick_upper used to look up the position match the burn event exactly","Rebuild the profiler state from genesis if position liquidity has diverged"],"exampleFix":"// before\nprofiler.process_burn(owner, tick_lower, tick_upper, amount)?;\n// after\nlet amount = amount.min(position_liquidity(owner, tick_lower, tick_upper));\nprofiler.process_burn(owner, tick_lower, tick_upper, amount)?;","handlingStrategy":"validation","validationCode":"let tracked = position_liquidity(owner, tick_lower, tick_upper);\nlet burn = burn_amount.min(tracked);\nprofiler.process_burn(owner, tick_lower, tick_upper, burn)?;","typeGuard":null,"tryCatchPattern":"match profiler.process_burn(owner, lower, upper, amount) {\n    Err(e) if e.to_string().contains(\"less than the requested burn amount\") => {\n        // event ordering/divergence: rebuild state from genesis\n    }\n    other => other?,\n}","preventionTips":["Process events strictly in block/log order so mints precede burns","Key positions by the exact (owner, tick_lower, tick_upper) from the event","Avoid replaying overlapping log ranges twice","Clamp burns to tracked liquidity when tolerating small discrepancies"],"tags":["rust","defi","uniswap-v3","liquidity","burn","replay"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}