nautechsystems/nautilus_trader · error
Cannot batch cancel orders for different instruments: {} vs
Error message
Cannot batch cancel orders for different instruments: {} vs {} What it means
All orders named in a batch cancel must belong to the same instrument. Each order's instrument_id is compared to the first order's; any mismatch aborts the batch because a BatchCancelOrders command is venue/instrument-scoped.
Source
Thrown at crates/trading/src/strategy/mod.rs:714
// TODO: Snapshot all orders from the cache. See `cancel_order` for the rationale.
let orders: Vec<OrderAny> = {
let cache_rc = StrategyNative::strategy_core_mut(self).cache_rc();
let cache = cache_rc.borrow();
client_order_ids
.iter()
.map(|id| {
cache
.try_order_owned(id)
.map_err(|e| anyhow::anyhow!("Cannot cancel order: {e}"))
})
.collect::<Result<_, _>>()?
};
let instrument_id = orders[0].instrument_id();
for order in &orders {
if order.instrument_id() != instrument_id {
anyhow::bail!(
"Cannot batch cancel orders for different instruments: {} vs {}",
instrument_id,
order.instrument_id()
);
}
if order.is_emulated() || order.is_active_local() {
anyhow::bail!("Cannot include emulated or local orders in batch cancel");
}
}
let mut cancels = Vec::with_capacity(orders.len());
for order in orders {
if !self.mark_order_pending_cancel(&order)? {
continue;
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Group client_order_ids by instrument_id and issue one cancel_orders call per instrument
- Verify the list only contains IDs from a single instrument (e.g. from strategy orders for that instrument)
- Use cancel_all_orders(instrument_id) per instrument instead of a mixed batch
Example fix
// before
strategy.cancel_orders(&all_ids, instrument_id, None, None).await?;
// after
for iid in all_instruments {
let ids: Vec<ClientOrderId> = all_orders.iter()
.filter(|o| o.instrument_id() == iid)
.map(|o| o.client_order_id())
.collect();
if !ids.is_empty() {
strategy.cancel_orders(&ids, iid, None, None).await?;
}
} Defensive patterns
Strategy: validation
Validate before calling
let resolved: Vec<_> = ids.iter().map(|id| cache.order(id)).collect::<Option<Vec<_>>>().unwrap_or_default(); let iid = resolved[0].instrument_id(); assert!(resolved.iter().all(|o| o.instrument_id() == iid), "batch cancel spans instruments");
Prevention
- Resolve orders and group by instrument before batching
- Track client_order_ids per instrument in strategy state
- Use per-instrument cancel_all_orders as a simpler alternative
When it happens
Trigger: Passing client_order_ids resolving to orders on two or more different instruments to `cancel_orders`.
Common situations: Cancelling 'all working orders' across symbols in one call; building the ID list from a multi-instrument cache scan; after a symbol change without regenerating the list.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot batch modify orders for different instruments: {} vs
- Order detail instrument mismatch for {instrument_id}: return
- Cannot batch modify empty order list
- Cannot include emulated or local orders in batch modify
- Cannot create command BatchModifyOrders: quantity, price, an
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/9d5c1b9dad497f88.
Report an issue: GitHub.