nautechsystems/nautilus_trader · error

Cannot persist position with no events: {}

Error message

Cannot persist position with no events: {}

What it means

When persisting a Position to the Redis cache, the adapter serializes the position's last event. This error means the Position has no event at all (`position.last_event()` returned None), so there is nothing to serialize. The library refuses to write a stateless position because a persisted position without events could not be reconstructed on load.

Source

Thrown at crates/infrastructure/src/redis/cache.rs:1264

        self.send_command(DatabaseOperation::Update, key, Some(vec![payload]))
    }

    fn serialize_account_event(&self, account: &AccountAny) -> anyhow::Result<Bytes> {
        let event: AccountState = account.last_event().ok_or_else(|| {
            anyhow::anyhow!("Cannot persist account with no events: {}", account.id())
        })?;
        let payload = DatabaseQueries::serialize_payload(self.encoding(), &event)?;
        Ok(Bytes::from(payload))
    }

    fn serialize_order_event(&self, order_event: &OrderEventAny) -> anyhow::Result<Bytes> {
        let payload = DatabaseQueries::serialize_payload(self.encoding(), order_event)?;
        Ok(Bytes::from(payload))
    }

    fn serialize_position_event(&self, position: &Position) -> anyhow::Result<Bytes> {
        let event: OrderFilled = position.last_event().ok_or_else(|| {
            anyhow::anyhow!("Cannot persist position with no events: {}", position.id)
        })?;
        let payload = DatabaseQueries::serialize_payload(self.encoding(), &event)?;
        Ok(Bytes::from(payload))
    }

    fn load_state(&self, key: String) -> anyhow::Result<AHashMap<String, Bytes>> {
        let mut con = self.database.con.clone();
        let trader_key = self.database.trader_key.clone();
        let encoding = self.encoding();
        let (tx, rx) = mpsc::channel();

        get_runtime().spawn(async move {
            let result = async {
                let full_key = format!("{trader_key}{REDIS_DELIMITER}{key}");
                let value: Option<Bytes> = con.get(&full_key).await?;
                let Some(value) = value else {
                    return Ok(AHashMap::new());
                };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the Position has been updated via `position.update(event)` (which sets `last_event`) before calling any cache persist/update API
  2. Check that order fill events are actually flowing to the position (event generation ordering in the execution engine or custom adapter)
  3. If migrating state, replay the original position events from the journal before persisting
  4. Log the position id to find which position is eventless and inspect how it was created

Example fix

// before
let position = Position::new(&event); // if created without events and persisted immediately
cache.update_position(&position)?;
// after
let mut position = Position::new(&event);
position.update(&fill_event); // ensures last_event is Some
cache.update_position(&position)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust
if position.last_event().is_none() {
    return Err(anyhow::anyhow!("refusing to persist position {} with no events", position.id));
}
cache.update_position(&position)?;

Type guard

fn has_events(position: &Position) -> bool {
    position.last_event().is_some()
}

Prevention

When it happens

Trigger: Calling `update_position(position)` (via the cache `update_actor`/position persist path) with a Position instance that was constructed but never received an event (e.g. a position initialized from a fill-less event or a manually built Position passed to `Position::update` was never invoked).

Common situations: Restoring or migrating positions from external data where events were not replayed; custom backtest/live code that constructs a Position directly and pushes it into the cache before processing any order-filled events; bugs in custom position handling in a strategy or custom adapter.

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/6b77ecf205f3dd1d. Report an issue: GitHub.