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

  1. Partition the orders by instrument_id and issue one modify_orders call per instrument
  2. Verify the collection/filter that builds the orders list only selects one instrument
  3. 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

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


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