nautechsystems/nautilus_trader · error
Cannot apply account state: balance would be negative {} {}
Error message
Cannot apply account state: balance would be negative {} {} (borrowing not allowed for {}) What it means
CashAccount::apply validates AccountState events the same way update_balances does: when allow_borrowing is false, any balance with a negative total causes the event application to fail. This keeps account state consistent with the no-borrowing constraint.
Source
Thrown at crates/model/src/accounts/cash.rs:190
impl Account for CashAccount {
impl_account_base_members!();
fn is_cash_account(&self) -> bool {
self.account_type == AccountType::Cash
}
fn is_margin_account(&self) -> bool {
self.account_type == AccountType::Margin
}
fn apply(&mut self, event: AccountState) -> anyhow::Result<()> {
self.check_event_account_id(&event)?;
if !self.allow_borrowing {
for balance in &event.balances {
if balance.total.raw < 0 {
anyhow::bail!(
"Cannot apply account state: balance would be negative {} {} \
(borrowing not allowed for {})",
balance.total.as_decimal(),
balance.currency.code,
self.id
);
}
}
}
// Only clear locks when the venue reports a fresh balance snapshot
if event.is_reported && !event.balances.is_empty() {
self.balances_locked.clear();
}
self.base_apply(event);
Ok(())
}View on GitHub (pinned to 18893faf8b)
Solutions
- Re-generate or clamp the AccountState balances to non-negative values
- Create the CashAccount with allow_borrowing=true if negative totals are legitimate
- Check that events are being routed to the correct account type
Example fix
// before: applying event to non-borrowing account
if !allow_borrowing { /* negative totals bail */ }
// after: guard before applying
let ok = event.balances.iter().all(|b| b.total.raw >= 0 || allow_borrowing);
anyhow::ensure!(ok, "negative balance with borrowing disabled");
account.apply(event)?; Defensive patterns
Strategy: validation
Validate before calling
if !acct.allows_borrowing() && event.balances.iter().any(|b| b.total.raw < 0) { return Err(anyhow!("negative balance event")); }
acct.apply(event)?; Type guard
fn event_appliable(acct: &CashAccount, event: &AccountState) -> bool {
acct.allows_borrowing() || event.balances.iter().all(|b| b.total.raw >= 0)
} Try / catch
if let Err(e) = acct.apply(event) {
if e.to_string().contains("borrowing not allowed") { /* regenerate event or enable borrowing */ }
} Prevention
- Keep account config consistent across event generation and replay
- Verify event sources target the correct account type
- Snapshot/restore flows should validate balances before apply
When it happens
Trigger: Calling apply() (or py_apply) with an AccountState event containing a negative balance total while the CashAccount has allow_borrowing=false.
Common situations: Replaying account state events generated under different account config (borrowing later disabled), restoring snapshots that include overdrafts, wiring events from a margin context into a cash account.
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
- Cash account balance would become negative: {} {} (borrowing
- 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/7db8da75717b999b.
Report an issue: GitHub.