nautechsystems/nautilus_trader · error · anyhow::Error
Cannot submit an empty order list
Error message
Cannot submit an empty order list
What it means
handle_submit_order_list_async validates upfront that the batch it was asked to submit contains at least one order via anyhow::ensure!. An empty order list would produce zero IB child orders and no meaningful batch submission, so it is rejected before taking the order-submit lock.
Source
Thrown at crates/adapters/interactive_brokers/src/execution/core_orders.rs:422
orders: &[OrderAny],
client: &Arc<Client>,
order_id_map: &Arc<Mutex<AHashMap<ClientOrderId, i32>>>,
venue_order_id_map: &Arc<Mutex<AHashMap<i32, ClientOrderId>>>,
instrument_id_map: &Arc<Mutex<AHashMap<i32, InstrumentId>>>,
trader_id_map: &Arc<Mutex<AHashMap<i32, TraderId>>>,
strategy_id_map: &Arc<Mutex<AHashMap<i32, StrategyId>>>,
active_order_contexts: &Arc<Mutex<AHashMap<i32, TrackedOrderContext>>>,
terminal_order_contexts: &Arc<Mutex<FifoCacheMap<i32, TrackedOrderContext, 10_000>>>,
next_order_id: &Arc<Mutex<i32>>,
instrument_provider: &Arc<InteractiveBrokersInstrumentProvider>,
exec_sender: &tokio::sync::mpsc::UnboundedSender<ExecutionEvent>,
clock: &'static AtomicTime,
account_id: AccountId,
strategy_id: StrategyId,
order_submit_lock: &Arc<tokio::sync::Mutex<()>>,
) -> anyhow::Result<()> {
let num_orders = orders.len();
anyhow::ensure!(!orders.is_empty(), "Cannot submit an empty order list");
let _submit_guard = order_submit_lock.lock().await;
let ib_account = account_id
.to_string()
.split_once('-')
.map_or_else(|| account_id.to_string(), |(_, value)| value.to_string());
let mut ib_order_ids = AHashMap::with_capacity(num_orders);
for order in orders {
let ib_order_id = Self::reserve_next_local_order_id(next_order_id)?;
ib_order_ids.insert(order.client_order_id(), ib_order_id);
}
for order in orders {
if let Some(parent_order_id) = order.parent_order_id()
&& !ib_order_ids.contains_key(&parent_order_id)
{
let map = order_id_map.lock();View on GitHub (pinned to 18893faf8b)
Solutions
- Guard the call site: skip calling handle_submit_order_list_async when orders.is_empty().
- Fix the batch-construction logic so at least one order survives filtering.
- Log the strategy state at the call site to see why the list ended up empty.
Example fix
// before
exec_client.handle_submit_order_list_async(orders, ...).await?;
// after
if !orders.is_empty() {
exec_client.handle_submit_order_list_async(orders, ...).await?;
} Defensive patterns
Strategy: validation
Validate before calling
anyhow::ensure!(!orders.is_empty(), "refusing to submit empty order batch");
Prevention
- Guard batch submission call sites with an is_empty check.
- Log when batch construction filters out every order.
- Unit-test batch builders with all-legs-filtered scenarios.
When it happens
Trigger: Calling handle_submit_order_list_async with an empty Vec of orders (e.g. a bracket/OCO batch builder that filtered out all orders, or a caller passing an unpopulated list).
Common situations: Strategy code building contingent order batches where all entries were dropped by conditional logic (e.g. every leg failed validation); refactors that moved filtering before batch construction.
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
- instrument_ratios list needs to have at least 2 legs
- ratio cannot be zero
- Timeout must be greater than 0
- Timeout must be less than 3600 seconds
- VNC port must be between 5900 and 5999
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ba8ad95fe2ff7626.
Report an issue: GitHub.