nautechsystems/nautilus_trader · error

wallet has no observed balance for {}

Error message

wallet has no observed balance for {}

What it means

update_balance_locked reserves balance for an instrument but requires that the wallet has already observed a balance in the reservation's currency. If self.base.balances has no entry for that currency, the reservation cannot be validated or normalized and this error is thrown. It enforces the invariant that locks apply only to currencies the wallet actually holds.

Source

Thrown at crates/model/src/accounts/wallet.rs:140

    /// Updates the locked balance for the given instrument and currency.
    ///
    /// # Errors
    ///
    /// Returns an error if `locked` is negative, the wallet has no observed balance for its
    /// currency, or the local reservations cannot produce a valid balance.
    pub fn update_balance_locked(
        &mut self,
        instrument_id: InstrumentId,
        locked: Money,
    ) -> anyhow::Result<()> {
        let current_balance = self
            .base
            .balances
            .get(&locked.currency)
            .copied()
            .ok_or_else(|| {
                anyhow::anyhow!("wallet has no observed balance for {}", locked.currency)
            })?;
        Self::validate_observed_balance(current_balance)?;
        let locked = Self::normalize_reservation(locked, current_balance.currency)?;
        let key = (instrument_id, current_balance.currency);
        let previous = self.balances_locked.remove_entry(&key);
        self.balances_locked.insert(key, locked);
        let balance = match Self::balance_from_locks_checked(current_balance, &self.balances_locked)
        {
            Ok(balance) => balance,
            Err(e) => {
                self.balances_locked.remove(&key);
                if let Some((previous_key, previous)) = previous {
                    self.balances_locked.insert(previous_key, previous);
                }

                return Err(e.into());
            }
        };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure an initial balance update for the currency is applied to the wallet before locking
  2. Check self.balances contains the currency before calling update_balance_locked
  3. Fix event ordering so balance events are processed before order-related locks

Example fix

// before
wallet.update_balance_locked(instrument_id, locked).unwrap();
// after
if !wallet.balances().contains_key(&locked.currency) {
    anyhow::bail!("no observed balance for {} yet", locked.currency);
}
wallet.update_balance_locked(instrument_id, locked)?;
Defensive patterns

Strategy: validation

Validate before calling

if wallet.balances().get(&locked.currency).is_none() {
    bail!("cannot lock {}: no observed balance", locked.currency);
}

Type guard

fn has_observed_balance(wallet: &AccountWallet, c: &Currency) -> bool {
    wallet.balances().contains_key(c)
}

Try / catch

match wallet.update_balance_locked(instrument_id, locked) {
    Ok(()) => ...,
    Err(e) if e.to_string().contains("no observed balance") => warn!("balance not yet observed, deferring lock"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling AccountWallet::update_balance_locked (directly or via apply of an event) with a AccountBalanceLocked whose currency was never reported via a balance update — e.g. locking funds in a currency before any balance event arrived.

Common situations: Submitting orders in a quote/base currency the wallet has not received a balance snapshot for; race at startup where order flow precedes the initial balance event; adapter not emitting balances for all currencies.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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