nautechsystems/nautilus_trader · error

Order invariant violated: no events

Error message

Order invariant violated: no events

What it means

OrderAny::init_event returns the first event, which by the order-model invariant is always OrderInitialized. It unwraps with expect("Order invariant violated: no events") and panics when the order's event list is empty, and with the sibling message when the first event is a different type. Since from_events rejects empty input, an OrderAny with no events should never exist — hitting this means the object was constructed or mutated outside the documented paths.

Source

Thrown at crates/model/src/orders/any.rs:113

                .map_err(|source| OrderReplayError::ApplyFailed { source })?;
        }

        Ok(order)
    }

    /// Returns a reference to the [`crate::events::OrderInitialized`] event.
    ///
    /// This is always the first event in the order's event list (invariant).
    ///
    /// # Panics
    ///
    /// Panics if the first event is not `OrderInitialized` (violates invariant).
    #[must_use]
    pub fn init_event(&self) -> &crate::events::OrderInitialized {
        match self
            .events()
            .first()
            .expect("Order invariant violated: no events")
        {
            OrderEventAny::Initialized(init) => init,
            _ => panic!("Order invariant violated: first event must be OrderInitialized"),
        }
    }

    /// Assigns the order to the list identified by `id`.
    ///
    /// # Panics
    ///
    /// Panics if the order has no events or its first event is not `OrderInitialized`.
    pub fn set_order_list_id(&mut self, id: OrderListId) {
        let order: &mut OrderCore = match self {
            Self::Limit(order) => order,
            Self::LimitIfTouched(order) => order,
            Self::Market(order) => order,
            Self::MarketIfTouched(order) => order,
            Self::MarketToLimit(order) => order,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Always construct OrderAny via OrderAny::from_events with OrderInitialized as the first event — it returns a proper OrderReplayError::EmptyInput instead of an order that panics later.
  2. Check the deserialization/restore path so the first persisted event (Initialized) is included and not filtered out.
  3. Verify no code clears or truncates the `events` vec on a live order.
  4. Where you cannot guarantee the invariant, call order.events().first() yourself and handle None before calling init_event.
  5. Prefer OrderInitialized event data directly from your creation site if you already hold it, rather than re-reading from the order.

Example fix

// before
let init = order.init_event(); // panics if events empty
// after
let init = order.events().first().map(|e| match e {
    OrderEventAny::Initialized(init) => init.clone(),
    _ => panic!("first event must be Initialized"),
});
match init {
    Some(init) => { /* use init */ }
    None => return Err("order has no events".into()),
}
Defensive patterns

Strategy: type-guard

Validate before calling

if order.events().is_empty() {
    return Err("order has no events; cannot read init_event".into());
}
let init = order.init_event();

Type guard

fn has_init_event(order: &OrderAny) -> bool {
    matches!(order.events().first(), Some(OrderEventAny::Initialized(_)))
}

Try / catch

// Prefer from_events, which returns Result instead of panicking:
let order = match OrderAny::from_events(events) {
    Ok(o) => o,
    Err(OrderReplayError::EmptyInput) => return Err("no order events".into()),
    Err(e) => return Err(e.into()),
};
let init = order.init_event();

Prevention

When it happens

Trigger: Calling init_event on an OrderAny that was built with zero events (e.g. via unsafe/Default-ish paths, manual struct construction, or code that cleared `events`), or on a partially deserialized order where the Initialized event was dropped.

Common situations: Custom deserialization that skips the init event; tests or tools constructing OrderAny fields directly; cache/restore code that applied events into a fresh order without seeding OrderInitialized; version-skewed persisted state.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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