nautechsystems/nautilus_trader · error
Cannot batch modify orders for different instruments: {} vs
Error message
Cannot batch modify orders for different instruments: {} vs {} What it means
All orders in a batch modify must belong to the same instrument. During validation, each order's instrument_id is compared with the first order's; a mismatch aborts the whole batch since a single BatchModifyOrders command cannot span instruments.
Source
Thrown at crates/trading/src/strategy/mod.rs:493
let orders: Vec<OrderAny> = {
let cache_rc = StrategyNative::strategy_core_mut(self).cache_rc();
let cache = cache_rc.borrow();
updates
.iter()
.map(|(client_order_id, _, _, _)| {
cache
.try_order_owned(client_order_id)
.map_err(|e| anyhow::anyhow!("Cannot modify order: {e}"))
})
.collect::<Result<_, _>>()?
};
let instrument_id = orders[0].instrument_id();
for (order, (_, quantity, price, trigger_price)) in orders.iter().zip(updates.iter()) {
if order.instrument_id() != instrument_id {
anyhow::bail!(
"Cannot batch modify 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 modify");
}
let mut updating = false;
if quantity.is_some_and(|q| q != order.quantity()) {
updating = true;
}
if let Some(price) = price {
if !LIMIT_ORDER_TYPES.contains(&order.order_type()) {View on GitHub (pinned to 18893faf8b)
Solutions
- Partition the orders by instrument_id and issue one modify_orders call per instrument
- Verify the collection/filter that builds the orders list only selects one instrument
- Add an assert or filter on `order.instrument_id() == instrument_id` before the call
Example fix
// before
strategy.modify_orders(&orders, &updates, None, None).await?;
// after
for (iid, group) in orders.iter().map(|o| o.instrument_id()).collect::<std::collections::HashSet<_>>() {
let subset: Vec<_> = orders.iter().filter(|o| o.instrument_id() == iid).collect();
strategy.modify_orders(&subset, &matching_updates, None, None).await?;
} Defensive patterns
Strategy: validation
Validate before calling
let iid = orders[0].instrument_id(); assert!(orders.iter().all(|o| o.instrument_id() == iid), "batch spans multiple instruments");
Type guard
fn same_instrument(orders: &[OrderAny]) -> bool {
orders.iter().map(|o| o.instrument_id()).all_equal()
} Try / catch
match strategy.modify_orders(&orders, &updates, None, None).await {
Err(e) if e.to_string().contains("different instruments") => { /* partition per instrument and retry */ }
r => r?,
} Prevention
- Collect orders per instrument_id, never across symbols
- Filter the cache query by instrument_id
- Add an assert before batch calls in strategy code
When it happens
Trigger: Passing orders for two or more different instrument_ids (e.g. AAPL.NASDAQ and MSFT.NASDAQ) in the same `orders` slice to `modify_orders`, even if updates align positionally.
Common situations: Collecting open orders from a cache across all instruments instead of per-instrument; loop variables capturing multiple symbols; merging order lists from different strategies or subscriptions.
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 cancel 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/b461e94ab5b9aa1a.
Report an issue: GitHub.