nautechsystems/nautilus_trader · error

Order in list denied: invalid status for {}, expected INITIA

Error message

Order in list denied: invalid status for {}, expected INITIALIZED

What it means

Every order in a submitted list must be in OrderStatus::Initialized. If any order in the list has already transitioned (submitted, accepted, modified, etc.), submit_order_list bails naming the offending client_order_id, since a list submit would resend a live/stale order.

Source

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

    /// or order list submission fails.
    fn submit_order_list(
        &mut self,
        mut orders: Vec<OrderAny>,
        position_id: Option<PositionId>,
        client_id: Option<ClientId>,
        params: Option<Params>,
    ) -> anyhow::Result<()>
    where
        Self: StrategyNative,
    {
        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(),
                );
            }
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Filter the list to orders with status INITIALIZED before submitting.
  2. Create fresh orders via the order factory for retries instead of reusing submitted objects.
  3. Track which orders were already submitted and exclude them from subsequent lists.

Example fix

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

// after
fresh = [o for o in orders if o.status == OrderStatus.INITIALIZED]
if fresh:
    self.submit_order_list(OrderList(fresh))
Defensive patterns

Strategy: validation

Validate before calling

fresh = [o for o in orders if o.status == OrderStatus.INITIALIZED]
if len(fresh) != len(orders):
    self.log.warning(f"dropping {len(orders) - len(fresh)} non-INITIALIZED orders from list")

Type guard

def all_initialized(orders) -> bool:
    return all(o.status == OrderStatus.INITIALIZED for o in orders)

Try / catch

try:
    self.submit_order_list(order_list)
except RuntimeError as e:
    if "invalid status" in str(e):
        self.log.error(f"order list contained submitted orders: {e}")
    else:
        raise

Prevention

When it happens

Trigger: Passing an order that was already submitted (or whose submit failed midway and is being retried) inside the orders vector; mixing freshly created orders with previously submitted ones in one list.

Common situations: Retry logic that resubmits the same order objects after a transient error; caching order objects across on_start invocations; replaying a batch that includes live orders.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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