{"record":{"id":"332f136181784f13","repo":"nautechsystems/nautilus_trader","slug":"ib-order-id-ib-order-id-is-already-mapped-to-cli","errorCode":null,"errorMessage":"IB order ID {ib_order_id} is already mapped to client order {existing_client_order_id}","messagePattern":"IB order ID (.+?) is already mapped to client order (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/execution/core_tracking.rs","lineNumber":218,"sourceCode":"    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub(super) fn cache_cancel_order_tracking(\n        ib_order_id: i32,\n        cmd: &CancelOrder,\n        target_order: &OrderAny,\n        order_id_map: &Arc<Mutex<AHashMap<ClientOrderId, i32>>>,\n        venue_order_id_map: &Arc<Mutex<AHashMap<i32, ClientOrderId>>>,\n        instrument_id_map: &Arc<Mutex<AHashMap<i32, InstrumentId>>>,\n        trader_id_map: &Arc<Mutex<AHashMap<i32, TraderId>>>,\n        strategy_id_map: &Arc<Mutex<AHashMap<i32, StrategyId>>>,\n    ) -> anyhow::Result<()> {\n        // Order-status callbacks first map the IB order ID to a client order ID, then read\n        // its instrument, trader, and strategy IDs. Hold this lock while updating all maps\n        // so a callback sees either the complete identity or no route at all.\n        let mut venue_map = venue_order_id_map.lock();\n        if let Some(existing_client_order_id) = venue_map.get(&ib_order_id) {\n            anyhow::ensure!(\n                *existing_client_order_id == cmd.client_order_id,\n                \"IB order ID {ib_order_id} is already mapped to client order {existing_client_order_id}\"\n            );\n        }\n        venue_map.remove(&ib_order_id);\n\n        order_id_map.lock().insert(cmd.client_order_id, ib_order_id);\n        instrument_id_map\n            .lock()\n            .insert(ib_order_id, target_order.instrument_id());\n        trader_id_map\n            .lock()\n            .insert(ib_order_id, target_order.trader_id());\n        strategy_id_map\n            .lock()\n            .insert(ib_order_id, target_order.strategy_id());\n        venue_map.insert(ib_order_id, cmd.client_order_id);\n","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/execution/core_tracking.rs#L200-L236","documentation":"cache_cancel_order_tracking updates the venue_order_id_map under a lock so order-status callbacks see a complete identity mapping. If the IB order ID being registered for a cancel already maps to a DIFFERENT client order ID, the adapter refuses to overwrite it via anyhow::ensure! — this indicates an identity collision or duplicate/mismatched cancel for that venue order.","triggerScenarios":"Calling cache_cancel_order_tracking with a command whose client_order_id does not match the client_order_id already mapped to ib_order_id in venue_order_id_map — e.g. two cancel commands with different client IDs for the same venue order, or a stale/mismatched ID mapping.","commonSituations":"Duplicate cancel submissions after a reconnect where IDs were remapped; strategy bugs reusing client order IDs; race between an order-status callback and a cancel when the lock discipline is bypassed; node restart with partially restored mappings.","solutions":["Verify the client_order_id in the cancel command matches the one originally submitted for that IB order.","Clear stale venue_order_id_map entries (re-sync order state) after reconnects/restarts.","Ensure all mapping updates go through the same locked path; audit for code writing venue maps without the lock.","Log both IDs at the collision to identify which component is issuing the mismatched cancel."],"exampleFix":"// before\ncancel_cmd.client_order_id = ClientOrderId::new(\"O-20260908-002\"); // mismatched\n// after\ncancel_cmd.client_order_id = original_submit_client_order_id; // must equal the mapped ID","handlingStrategy":"validation","validationCode":"let mapped = venue_order_id_map.lock().get(&ib_order_id).cloned();\nif let Some(existing) = mapped {\n    anyhow::ensure!(existing == cmd.client_order_id, \"cancel client_order_id {} != mapped {}\", cmd.client_order_id, existing);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always cancel using the client_order_id returned from the original submission.","Re-sync venue/order maps after reconnects before issuing cancels.","Never reuse client order IDs across orders."],"tags":["interactive-brokers","order-tracking","id-collision","concurrency","rust"],"backgroundTag":"internal-invariant-violation","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"}