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
- Check !orders.is_empty() (or after filtering) before calling from_orders
- Return early / skip list construction entirely when no orders remain
- 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
- Filter batches first, then skip OrderList construction when nothing remains
- Centralize submission batching in one function that owns the empty check
- Add debug_assert!(!orders.is_empty()) in callers that feed from_orders
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.