nautechsystems/nautilus_trader · error · anyhow::Error

IB order ID {ib_order_id} is already mapped to client order

Error message

IB order ID {ib_order_id} is already mapped to client order {existing_client_order_id}

What it means

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.

Source

Thrown at crates/adapters/interactive_brokers/src/execution/core_tracking.rs:218

    }

    #[allow(clippy::too_many_arguments)]
    pub(super) fn cache_cancel_order_tracking(
        ib_order_id: i32,
        cmd: &CancelOrder,
        target_order: &OrderAny,
        order_id_map: &Arc<Mutex<AHashMap<ClientOrderId, i32>>>,
        venue_order_id_map: &Arc<Mutex<AHashMap<i32, ClientOrderId>>>,
        instrument_id_map: &Arc<Mutex<AHashMap<i32, InstrumentId>>>,
        trader_id_map: &Arc<Mutex<AHashMap<i32, TraderId>>>,
        strategy_id_map: &Arc<Mutex<AHashMap<i32, StrategyId>>>,
    ) -> anyhow::Result<()> {
        // Order-status callbacks first map the IB order ID to a client order ID, then read
        // its instrument, trader, and strategy IDs. Hold this lock while updating all maps
        // so a callback sees either the complete identity or no route at all.
        let mut venue_map = venue_order_id_map.lock();
        if let Some(existing_client_order_id) = venue_map.get(&ib_order_id) {
            anyhow::ensure!(
                *existing_client_order_id == cmd.client_order_id,
                "IB order ID {ib_order_id} is already mapped to client order {existing_client_order_id}"
            );
        }
        venue_map.remove(&ib_order_id);

        order_id_map.lock().insert(cmd.client_order_id, ib_order_id);
        instrument_id_map
            .lock()
            .insert(ib_order_id, target_order.instrument_id());
        trader_id_map
            .lock()
            .insert(ib_order_id, target_order.trader_id());
        strategy_id_map
            .lock()
            .insert(ib_order_id, target_order.strategy_id());
        venue_map.insert(ib_order_id, cmd.client_order_id);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the client_order_id in the cancel command matches the one originally submitted for that IB order.
  2. Clear stale venue_order_id_map entries (re-sync order state) after reconnects/restarts.
  3. Ensure all mapping updates go through the same locked path; audit for code writing venue maps without the lock.
  4. Log both IDs at the collision to identify which component is issuing the mismatched cancel.

Example fix

// before
cancel_cmd.client_order_id = ClientOrderId::new("O-20260908-002"); // mismatched
// after
cancel_cmd.client_order_id = original_submit_client_order_id; // must equal the mapped ID
Defensive patterns

Strategy: validation

Validate before calling

let mapped = venue_order_id_map.lock().get(&ib_order_id).cloned();
if let Some(existing) = mapped {
    anyhow::ensure!(existing == cmd.client_order_id, "cancel client_order_id {} != mapped {}", cmd.client_order_id, existing);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/332f136181784f13. Report an issue: GitHub.