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
- Enable borrowing at account construction if negative balances are expected
- Correct the balance data so totals stay non-negative
- 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
- Match allow_borrowing to the account's realistic cash flows
- Clamp or reject overdrawing withdrawals upstream
- Log near-zero balances to detect borrowing needs early
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
- Cannot apply account state: balance would be negative {} {}
- Cannot apply betting account state: balance would be negativ
- cannot rebuild Wallet reservations for account {account_id}
- Failed to convert balance: {e}
- locked balance was negative: {locked}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/eb3c74d5326201db.
Report an issue: GitHub.