{"record":{"id":"b0fd00fd609e48e7","repo":"nautechsystems/nautilus_trader","slug":"stale-position-fill-void-for","errorCode":null,"errorMessage":"stale position fill void for {}","messagePattern":"stale position fill void for (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/position.rs","lineNumber":758,"sourceCode":"        commission_voided: Option<Money>,\n    ) -> anyhow::Result<Option<Money>> {\n        let fragment_qty = self\n            .fill_fragments(event.client_order_id, event.trade_id)\n            .iter()\n            .fold(Quantity::zero(self.size_precision), |total, fill| {\n                total + fill.last_qty\n            });\n        anyhow::ensure!(\n            !voided_qty.is_zero() && voided_qty <= fragment_qty,\n            \"position fill void exceeds known fragments for {}\",\n            event.trade_id,\n        );\n\n        if let Some(previous) = self.fill_voids.iter().rev().find(|record| {\n            record.event.client_order_id == event.client_order_id\n                && record.event.trade_id == event.trade_id\n        }) {\n            anyhow::ensure!(\n                voided_qty >= previous.voided_qty,\n                \"stale position fill void for {}\",\n                event.trade_id,\n            );\n            anyhow::ensure!(\n                voided_qty != previous.voided_qty\n                    || commission_voided != previous.commission_voided,\n                \"duplicate position fill void for {}\",\n                event.trade_id,\n            );\n        }\n\n        self.fill_voids.push(PositionFillVoid {\n            event,\n            voided_qty,\n            commission_voided,\n        });\n","sourceCodeStart":740,"sourceCodeEnd":776,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/position.rs#L740-L776","documentation":"Position::apply_fill_void rejects a void event whose voided_qty is smaller than a previously recorded void for the same client_order_id/trade_id pair. Fill voids must be monotonically non-decreasing (they represent cumulative voided quantity), so an out-of-order or stale void event would rewind state. This protects against replaying old events after newer ones.","triggerScenarios":"Applying the same void event stream out of order, e.g. replaying historical events where an older cumulative-void record arrives after a larger one for the same trade_id/client_order_id; re-processing a backfill snapshot after live voids were already applied.","commonSituations":"Backtesting with a replay engine that does not preserve event ordering; loading a stale cached position snapshot and re-applying old void events; duplicated data feeds delivering events with delayed timestamps.","solutions":["Deliver fill-void events in the original venue/event ordering (sort by event timestamp/sequence) before applying.","Check the existing fill_voids records for the same client_order_id/trade_id and skip events with voided_qty less than the latest recorded value.","Rebuild the position from scratch rather than mixing a stale snapshot with newer live void events."],"exampleFix":"// before: replaying old events onto a live position\nfor ev in stale_history { position.apply_fill_void(ev)?; }\n// after: skip stale voids\nlet last = position.fill_voids.iter().rev().find(|r|\n    r.event.client_order_id == ev.client_order_id && r.event.trade_id == ev.trade_id);\nif let Some(prev) = last {\n    if ev.voided_qty < prev.voided_qty { continue; } // stale, ignore\n}\nposition.apply_fill_void(ev)?;","handlingStrategy":"validation","validationCode":"if let Some(prev) = position.fill_voids.iter().rev().find(|r| {\n    r.event.client_order_id == ev.client_order_id && r.event.trade_id == ev.trade_id\n}) {\n    if ev.voided_qty < prev.voided_qty { return Ok(()); } // stale, skip\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Preserve event ordering when replaying or backfilling void events.","Never mix a stale position snapshot with newer live void events.","Sort void events by timestamp/sequence before application."],"tags":["position","pnl","event-ordering","rust"],"backgroundTag":"invalid-state-transition","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"}