nautechsystems/nautilus_trader · error
locked balance was negative: {locked}
Error message
locked balance was negative: {locked} What it means
`update_balance_locked` reserves a portion of an account balance for a pending order, and a negative locked amount is nonsensical — it would imply releasing more than was reserved or creating balance from nothing. The function rejects the update before mutating any state.
Source
Thrown at crates/model/src/accounts/base.rs:432
/// Updates the locked balance for the given instrument and currency, then recalculates the
/// account balance for that currency from all per-(instrument, currency) locks.
///
/// The reservation is recorded without a balance when the currency has no observed balance yet,
/// so a later balance report derives from it.
///
/// # Errors
///
/// Returns an error if `locked` is negative, its precision differs from the balance precision,
/// or the reservations cannot produce a valid balance. Balances and reservations are left
/// unchanged when an error is returned.
pub(crate) fn update_balance_locked(
balances: &mut IndexMap<Currency, AccountBalance>,
balances_locked: &mut AHashMap<(InstrumentId, Currency), Money>,
instrument_id: InstrumentId,
locked: Money,
) -> anyhow::Result<()> {
anyhow::ensure!(locked.raw >= 0, "locked balance was negative: {locked}");
let currency = locked.currency;
let key = (instrument_id, currency);
let Some(current_balance) = balances.get(¤cy).copied() else {
balances_locked.insert(key, locked);
return Ok(());
};
anyhow::ensure!(
current_balance.currency.precision == currency.precision,
"Cannot update {currency} reservation: precision {} differed from balance precision {}",
currency.precision,
current_balance.currency.precision
);
let previous = balances_locked.insert(key, locked);
View on GitHub (pinned to 18893faf8b)
Solutions
- Fix the caller to pass a non-negative locked amount
- Check adapter logic that derives locked balance from total vs available so signs are correct
- If reducing a reservation, use the release/unlock path instead of passing a negative value
Example fix
// before update_balance_locked(balances, locks, instrument_id, Money(-100, USD)) // after update_balance_locked(balances, locks, instrument_id, Money(100, USD))
Defensive patterns
Strategy: validation
Validate before calling
if locked.raw < 0:
raise ValueError(f"cannot lock negative balance: {locked}") Try / catch
try:
update_balance_locked(balances, locks, instrument_id, locked)
except Exception as e:
log.warning(f"balance lock rejected: {e}") # state unmutated Prevention
- Compute locked amounts as absolute values
- Use release/unlock paths rather than negative locks
- Audit adapters' derivation of held vs available balances
When it happens
Trigger: Calling update_balance_locked (or the account's balance-locking path) with a locked Money whose raw value is negative, e.g. when an adapter computes locked balance as a negative delta.
Common situations: Adapter balance-sync bugs where held/margin amounts are subtracted twice, sign errors when converting broker 'available' vs 'locked' fields, or test code constructing negative Money.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- {e}
- start must not be later than end
- limit must be greater than zero
- Cannot apply betting account state: balance would be negativ
- Cash account balance would become negative: {} {} (borrowing
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/caff85a88e3fb732.
Report an issue: GitHub.