nautechsystems/nautilus_trader · error
Cannot persist account with no events: {}
Error message
Cannot persist account with no events: {} What it means
Raised by `account_last_event` when an `AccountAny` has no events, i.e. `account.last_event()` returns None. The SQL cache persists accounts by writing their latest `AccountState` event via `add_account`/`update_account`; an account with an empty event log cannot be represented in the database, so persisting it is treated as an internal invariant violation and aborted with this anyhow error.
Source
Thrown at crates/infrastructure/src/sql/cache.rs:1318
) -> anyhow::Result<()> {
let mut snapshot = if position.fill_voids.is_empty() {
PositionSnapshot::from(position, unrealized_pnl)
} else {
PositionSnapshot::from_replay_state(position, unrealized_pnl)
};
snapshot.ts_init = ts_snapshot;
self.add_position_snapshot(&snapshot)
}
fn heartbeat(&self, _timestamp: UnixNanos) -> anyhow::Result<()> {
todo!()
}
}
fn account_last_event(account: &AccountAny) -> anyhow::Result<AccountState> {
account
.last_event()
.ok_or_else(|| anyhow::anyhow!("Cannot persist account with no events: {}", account.id()))
}
fn order_initialized_event(order: &OrderAny) -> OrderInitialized {
order.init_event().clone()
}
fn position_last_event(position: &Position) -> anyhow::Result<OrderFilled> {
position
.last_event()
.ok_or_else(|| anyhow::anyhow!("Cannot persist position with no events: {}", position.id))
}
#[expect(
clippy::too_many_lines,
reason = "database command dispatch enumerates each cache query variant explicitly"
)]
async fn drain_buffer(pool: &PgPool, buffer: &mut VecDeque<DatabaseQuery>) {
for cmd in buffer.drain(..) {View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure every account receives its initial AccountState event (apply it) before calling add_account/update_account on the cache.
- Check account.last_event().is_some() before persisting; if None, skip persistence or defer until the first event arrives.
- Trace the account id from the error message to where it was constructed and fix the code path that failed to apply events.
- Verify reconstruction/deserialization logic isn't dropping the account's event log.
Example fix
// before
cache.add_account(&account).await?;
// after
if account.last_event().is_some() {
cache.add_account(&account).await?;
} else {
tracing::warn!("skipping persistence of event-less account {}", account.id());
} Defensive patterns
Strategy: validation
Validate before calling
if account.last_event().is_none() {
return Err(anyhow::anyhow!(
"refusing to persist account {} with no events",
account.id()
));
} Type guard
fn has_events(account: &AccountAny) -> bool {
account.last_event().is_some()
} Try / catch
match cache.add_account(&account).await {
Err(e) if e.to_string().contains("no events") => {
tracing::warn!("skipping event-less account {}: {e}", account.id());
}
result => result?,
} Prevention
- Only construct accounts through the event-generating account flow so the initial AccountState event always exists.
- Check last_event().is_some() before any cache persistence call.
- In restore/replay paths, assert the account event log is non-empty before caching.
- Log account.id() alongside persistence failures to speed up tracing the origin.
When it happens
Trigger: Calling `cache.add_account(account)` or `cache.update_account(account)` (PostgresCacheAdapter) with an account that has never had an `AccountState` event applied: a hand-constructed account object, a deserialized/reconstructed account whose event log was dropped, or an adapter that failed to emit the initial state event.
Common situations: Custom broker/adapter code that builds an `AccountAny` manually in tests and registers it with the cache before any state update; restoring accounts from an external system without replaying their initial state event; a bug in an event-generating client that never publishes the first AccountState.
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
- Account {account_id} not found after cache update
- Cannot persist position with no events: {}
- Verified finality requires decision evidence
- DataActor {} must be registered before calling `cache()` - t
- Order {client_order_id} not found
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/fa226fde7f8a438b.
Report an issue: GitHub.