nautechsystems/nautilus_trader · error

OrderList denied: orders must share the same venue; expected

Error message

OrderList denied: orders must share the same venue; expected {first_venue}, found {} on {}

What it means

All orders in one OrderList must target the same venue. submit_order_list takes the venue of the first order and bails if any subsequent order's instrument_id.venue differs, because a list is processed as a single atomic submission to one execution venue.

Source

Thrown at crates/trading/src/strategy/mod.rs:221

    {
        if orders.is_empty() {
            log::error!("OrderList denied: no orders to submit");
            anyhow::bail!("OrderList denied: no orders to submit");
        }

        for order in &orders {
            if order.status() != OrderStatus::Initialized {
                anyhow::bail!(
                    "Order in list denied: invalid status for {}, expected INITIALIZED",
                    order.client_order_id()
                );
            }
        }

        let first_venue = orders[0].instrument_id().venue;
        for order in &orders {
            if order.instrument_id().venue != first_venue {
                anyhow::bail!(
                    "OrderList denied: orders must share the same venue; \
                     expected {first_venue}, found {} on {}",
                    order.instrument_id().venue,
                    order.client_order_id(),
                );
            }
        }

        let should_deny = {
            let core = StrategyNative::strategy_core_mut(self);
            let tag = core.market_exit_tag;
            core.is_exiting
                && orders.iter().any(|o| {
                    !o.is_reduce_only() && !o.tags().is_some_and(|tags| tags.contains(&tag))
                })
        };

        if should_deny {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Partition orders by instrument_id.venue and submit one OrderList per venue.
  2. Group orders by instrument before batching so a list never spans venues.
  3. Assert all orders share the first order's venue before calling submit_order_list.

Example fix

// before
self.submit_order_list(OrderList(all_orders))

// after
from itertools import groupby
for venue, group in groupby(sorted(all_orders, key=lambda o: o.instrument_id.venue), key=lambda o: o.instrument_id.venue):
    self.submit_order_list(OrderList(list(group)))
Defensive patterns

Strategy: validation

Validate before calling

venues = {o.instrument_id.venue for o in orders}
if len(venues) > 1:
    raise ValueError(f"order list spans venues: {venues}; submit one list per venue")

Type guard

def single_venue(orders) -> bool:
    return len({o.instrument_id.venue for o in orders}) <= 1

Try / catch

try:
    self.submit_order_list(order_list)
except RuntimeError as e:
    if "must share the same venue" in str(e):
        self.log.error("split batch by venue and resubmit")
    else:
        raise

Prevention

When it happens

Trigger: Passing orders for instruments on two or more venues (e.g. BINANCE and BYBIT, or an equities venue and a futures venue) in a single submit_order_list call.

Common situations: Multi-venue strategies batching orders from an aggregation loop; a grouping bug where orders are collected across instruments without partitioning by venue.

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/b524cc18272a7f19. Report an issue: GitHub.