{"record":{"id":"b8118db515e0568d","repo":"nautechsystems/nautilus_trader","slug":"fill-without-a-venue-position-id-would-cross-po","errorCode":null,"errorMessage":"fill {} without a venue position ID would cross position {position_id}","messagePattern":"fill (.+?) without a venue position ID would cross position (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/live/src/execution/manager.rs","lineNumber":2903,"sourceCode":"        let position = cache.position(&position_id).ok_or_else(|| {\n            anyhow::anyhow!(\n                \"fill {} maps to position {position_id}, which is not cached\",\n                report.trade_id,\n            )\n        })?;\n\n        anyhow::ensure!(\n            position.account_id == report.account_id\n                && position.instrument_id == report.instrument_id,\n            \"fill {} maps to position {position_id} with a different account or instrument\",\n            report.trade_id,\n        );\n        anyhow::ensure!(\n            position.is_open(),\n            \"fill {} maps to non-open position {position_id}\",\n            report.trade_id,\n        );\n        anyhow::ensure!(\n            !position.is_opposite_side(report.order_side) || report.last_qty <= position.quantity,\n            \"fill {} without a venue position ID would cross position {position_id}\",\n            report.trade_id,\n        );\n\n        report.venue_position_id = Some(position_id);\n        Ok(PositionFillReportPreparation::Ready)\n    }\n\n    #[cfg(feature = \"node\")]\n    fn has_active_inferred_fill(order: &OrderAny) -> anyhow::Result<bool> {\n        let events = order.events();\n        let trade_ids = order.trade_ids();\n        let Some((first, remaining)) = events.split_first() else {\n            return Ok(false);\n        };\n        let mut projected = OrderAny::from_events(vec![(*first).clone()]).map_err(|e| {\n            anyhow::anyhow!(","sourceCodeStart":2885,"sourceCodeEnd":2921,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/live/src/execution/manager.rs#L2885-L2921","documentation":"When a fill has no venue_position_id, the manager infers its position from the order. This check rejects an inferred fill whose side is opposite the position's side with quantity exceeding the position's open quantity - meaning the fill would flip/cross the position rather than reduce it, which the no-position-ID inference path does not allow.","triggerScenarios":"report.venue_position_id is None, the order maps to position_id, the fill side is opposite to position.side, and report.last_qty > position.quantity. Happens when a closing order over-fills past flat (venue supports position flip) but the adapter provides no position ID, or when the cached position quantity is stale/undersized.","commonSituations":"Trading venues that allow flipping a position in one fill while the adapter doesn't report position IDs; stale cached position quantity after missed fills; submitting a larger closing order than the open position without letting the adapter attribute the resulting fill.","solutions":["Have the adapter populate venue_position_id on fills so attribution is explicit instead of inferred","Ensure cached position quantity is accurate via reconciliation before sending oversized closing orders","Size closing orders to the actual open position quantity, or split orders so no fill crosses flat","Configure hedging mode if the strategy intentionally holds opposite-side exposure"],"exampleFix":"// before (oversized close, no position ID)\nlet qty = Quantity::from(2 * position.quantity);\n// after\nlet qty = position.quantity; // close exactly, avoid crossing","handlingStrategy":"validation","validationCode":"if report.venue_position_id.is_none() {\n    if let Some(pos) = cache.position(&position_id) {\n        let would_cross = pos.is_opposite_side(report.order_side) && report.last_qty > pos.quantity;\n        assert!(!would_cross);\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Have the adapter always report venue_position_id when the venue provides one","Size closing orders to the cached open quantity","Reconcile position quantities after missed-fill incidents","Use hedging mode for strategies that intentionally reverse exposure"],"tags":["rust","live-execution","position-inference","quantity-mismatch"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}