nautechsystems/nautilus_trader · error

Account {account_id} not found after cache update

Error message

Account {account_id} not found after cache update

What it means

`update_account_owned` writes the account into the in-memory cache, then needs it (as a shared cell) to persist to the backing database. If the account cannot be found in `self.accounts` immediately after `cache_account_owned`, an internal invariant was violated (the cache-upsert failed to store it), so the update aborts before touching the database.

Source

Thrown at crates/common/src/cache/mod.rs:5232

            Some(account_cell) => *account_cell.borrow_mut() = account,
            None => {
                self.accounts.insert(account_id, SharedCell::new(account));
            }
        }
    }

    /// Updates the `account` in the cache, taking ownership of the updated account.
    ///
    /// # Errors
    ///
    /// Returns an error if updating the account in the database fails.
    pub fn update_account_owned(&mut self, account: AccountAny) -> anyhow::Result<()> {
        let account_id = account.id();
        self.cache_account_owned(account);

        if let Some(database) = &mut self.database {
            let Some(account_cell) = self.accounts.get(&account_id) else {
                anyhow::bail!("Account {account_id} not found after cache update");
            };
            database.update_account(&account_cell.borrow())?;
        }
        Ok(())
    }

    /// Applies an account state event to the cached account.
    ///
    /// Mutates the cached account in place to avoid cloning the account event
    /// history on the hot path; long-running sessions accumulate many events
    /// per account, so a snapshot-clone here would be O(history) per update.
    ///
    /// # Errors
    ///
    /// Returns an error if applying or persisting the account state fails.
    pub fn update_account_state(&mut self, event: &AccountState) -> anyhow::Result<()> {
        let Some(cell) = self.accounts.get(&event.account_id) else {
            return self.add_account(AccountAny::from_events(std::slice::from_ref(event))?);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the account is added via the supported `add_account`/`update_account` path so `cache_account_owned` succeeds.
  2. Verify the account's AccountId is stable and unique; do not mutate it after construction.
  3. Rebuild/reload the cache if its internal state was cleared concurrently; check for multi-owner access to the cache.
Defensive patterns

Strategy: try-catch

Validate before calling

if cache.account(&account_id).is_none() {
    // add the account via the supported path before update_account_owned
}

Try / catch

if let Err(e) = cache.update_account_owned(account) {
    // treat as internal invariant violation: log state, consider cache reload
}

Prevention

When it happens

Trigger: Calling `update_account_owned` when `cache_account_owned` failed to insert the account (e.g. an account with a colliding or malformed AccountId, or corrupted cache state); a database-backed cache whose account map was cleared concurrently by another owner.

Common situations: Persistent (backed) caches where an account was removed between the write and the read; custom account types whose `id()` collides or changes; bespoke code paths inserting accounts without going through the cache.

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