nautechsystems/nautilus_trader · error

OrderList::from_orders requires non-empty orders

Error message

OrderList::from_orders requires non-empty orders

What it means

OrderList::from_orders constructs an OrderList from a slice of orders and needs a first order to derive the list id, instrument, and venue. It panics when given an empty slice; the doc comment notes callers are expected to filter out the empty case upstream.

Source

Thrown at crates/model/src/orders/list.rs:125

    /// the list may target different instruments at the same venue.
    /// Callers in the production path (`OrderFactory` plus a single
    /// strategy instance) produce orders with a consistent `order_list_id`
    /// and `strategy_id`. [`OrderList::validate`] checks the syntactic
    /// invariants (non-empty, unique `client_order_ids`); it does not
    /// check cross-field consistency.
    ///
    /// # Panics
    ///
    /// Panics if `orders` is empty, if the first order has no
    /// `order_list_id`, or if orders span more than one venue. Callers
    /// are expected to guard non-empty input; `Strategy::submit_order_list`
    /// filters out the empty case and bails on mixed venues before
    /// reaching this constructor.
    #[must_use]
    pub fn from_orders(orders: &[OrderAny], ts_init: UnixNanos) -> Self {
        let first = orders
            .first()
            .expect("OrderList::from_orders requires non-empty orders");
        let order_list_id = first
            .order_list_id()
            .expect("OrderList::from_orders requires first order to have order_list_id");
        let instrument_id = first.instrument_id();
        let strategy_id = first.strategy_id();
        let venue = instrument_id.venue;

        for order in orders {
            assert!(
                order.instrument_id().venue == venue,
                "OrderList::from_orders requires all orders to share the same venue; \
                 expected {venue}, found {} on {}",
                order.instrument_id().venue,
                order.client_order_id(),
            );
        }

        let client_order_ids = orders.iter().map(Order::client_order_id).collect();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check !orders.is_empty() (or after filtering) before calling from_orders
  2. Return early / skip list construction entirely when no orders remain
  3. Route empty batches through a different path that does not require an OrderList

Example fix

// before
let list = OrderList::from_orders(&pending, ts_init);
// after
if pending.is_empty() {
    return Ok(None);
}
let list = OrderList::from_orders(&pending, ts_init);
Defensive patterns

Strategy: validation

Validate before calling

// Rust
if orders.is_empty() { return Ok(None); } // before from_orders
let list = OrderList::from_orders(orders, ts_init);

Type guard

fn non_empty(orders: &[OrderAny]) -> Option<&[OrderAny]> { if orders.is_empty() { None } else { Some(orders) } }

Prevention

When it happens

Trigger: Calling OrderList::from_orders(&[], ts_init) — e.g. building order lists from a batch that produced zero orders.

Common situations: Batching submitted orders per venue where a filter (time-in-force, price checks) removed all orders; strategies submitting after a cancel-all emptied the pending buffer; feeding an empty container from Python-side code.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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