nautechsystems/nautilus_trader · error

Cash account balance would become negative: {} {} (borrowing

Error message

Cash account balance would become negative: {} {} (borrowing not allowed for {})

What it means

CashAccount::update_balances rejects negative balance totals when the account was created with allow_borrowing=false, since a cash account without borrowing cannot go below zero. The error reports the total, currency, and account id.

Source

Thrown at crates/model/src/accounts/cash.rs:136

        base::clear_balance_locked(
            &mut self.base.balances,
            &mut self.balances_locked,
            instrument_id,
        );
    }

    /// Updates the account balances, enforcing borrowing constraints.
    ///
    /// # Errors
    ///
    /// Returns an error if `allow_borrowing` is false and any balance has a negative total.
    ///
    /// TODO: Force stop backtest engine on error (like Python's `set_backtest_force_stop`)
    pub fn update_balances(&mut self, balances: &[AccountBalance]) -> anyhow::Result<()> {
        if !self.allow_borrowing {
            for balance in balances {
                if balance.total.raw < 0 {
                    anyhow::bail!(
                        "Cash account balance would become negative: {} {} (borrowing not allowed for {})",
                        balance.total.as_decimal(),
                        balance.currency.code,
                        self.id
                    );
                }
            }
        }
        self.base.update_balances(balances);
        Ok(())
    }

    #[must_use]
    pub fn is_cash_account(&self) -> bool {
        self.account_type == AccountType::Cash
    }

    #[must_use]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Enable borrowing at account construction if negative balances are expected
  2. Correct the balance data so totals stay non-negative
  3. Route flows requiring negative balances to a margin account instead

Example fix

// before
let mut acct = CashAccount::new(..., false); // allow_borrowing=false
acct.update_balances(&balances_with_negative_total)?; // bails
// after
let mut acct = CashAccount::new(..., true); // allow_borrowing=true
acct.update_balances(&balances_with_negative_total)?;
Defensive patterns

Strategy: validation

Validate before calling

if !acct.allows_borrowing() && balances.iter().any(|b| b.total.raw < 0) { /* reject or enable borrowing */ }
acct.update_balances(balances)?;

Type guard

fn balances_non_negative(bs: &[AccountBalance]) -> bool {
    bs.iter().all(|b| b.total.raw >= 0)
}

Try / catch

match acct.update_balances(balances) {
    Err(e) if e.to_string().contains("would become negative") => { /* enable borrowing or clamp */ }
    other => other?,
}

Prevention

When it happens

Trigger: Calling update_balances() on a CashAccount with allow_borrowing=false and any AccountBalance in the slice whose total.raw < 0.

Common situations: Backtest engines applying withdrawal/fee events that overdraw a non-borrowing cash account; configuring an account without borrowing then routing margin-like flows through it.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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