nautechsystems/nautilus_trader · error
invalid {name}: must be non-zero
Error message
invalid {name}: must be non-zero What it means
check_nonzero_denominator is a guard against division by zero in Bet arithmetic. It accepts a Decimal and its field name, and bails if the value is zero, so callers like as_bet_checked, decreased_state, flattening_bet_checked, and checked_div fail fast with a message naming the offending quantity instead of panicking or producing NaN/infinity.
Source
Thrown at crates/model/src/data/bet.rs:759
probability: Decimal,
volume: Decimal,
side: OrderSide,
) -> anyhow::Result<Bet> {
check_probability_invertible(probability)?;
let inverse_probability = checked_sub(Decimal::ONE, probability)?;
probability_to_bet(inverse_probability, volume, side.opposite())
}
fn check_odds_gt_one(price: Decimal) -> anyhow::Result<()> {
if price <= Decimal::ONE {
anyhow::bail!("Price must be greater than 1.0 for lay liability calculation, was {price}");
}
Ok(())
}
fn check_nonzero_denominator(value: Decimal, name: &str) -> anyhow::Result<()> {
if value.is_zero() {
anyhow::bail!("invalid {name}: must be non-zero")
}
Ok(())
}
fn checked_add(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_add(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow adding {lhs} and {rhs}"))
}
fn checked_sub(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_sub(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow subtracting {rhs} from {lhs}"))
}
fn checked_mul(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_mul(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow multiplying {lhs} by {rhs}"))
}View on GitHub (pinned to 18893faf8b)
Solutions
- Check the denominator with .is_zero() before the operation and handle the zero case explicitly (skip, treat result as zero, or error).
- Filter out zero-volume bets/orders before running bet state transitions or divisions.
- Verify the upstream data: a zero denominator often means the position is already flat and the operation is unnecessary.
Example fix
// before
let ratio = checked_div(pnl, remaining_volume)?;
// after
if remaining_volume.is_zero() {
return Ok(Decimal::ZERO); // nothing remains to normalize against
}
let ratio = checked_div(pnl, remaining_volume)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust
use rust_decimal::Decimal;
fn safe_div(n: Decimal, d: Decimal) -> Option<Decimal> { if d.is_zero() { None } else { Some(n / d) } } Try / catch
// Rust
match as_bet_checked(volume) {
Ok(bet) => bet,
Err(e) if e.to_string().contains("must be non-zero") => handle_flat_position(),
Err(e) => return Err(e),
} Prevention
- Always test .is_zero() on denominators before division.
- Skip state transitions on fully filled / zero-remaining-volume bets.
- Validate that feed sizes are positive before constructing bets.
When it happens
Trigger: Any bet operation that divides by a user-supplied or computed denominator (volume, price, size) when that value is exactly Decimal::ZERO — e.g. checked_div with rhs = 0, as_bet_checked with zero size, decreased_state / flattening_bet_checked after the bet was fully filled or cancelled to zero volume.
Common situations: Operating on a bet whose remaining volume reached zero; passing an unfilled/zero Quantity placeholder; a data feed supplying 0 size for an unmatched order.
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
- Polymarket market BUY amount {} pUSD truncates to zero at {L
- invalid probability: must not be 1.0 (inverse would be zero)
- Price must be greater than 1.0 for lay liability calculation
- AX minimum_order_size must be a positive whole number, was {
- AX initial_margin_pct must be positive, was {initial_margin_
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/efc96ce9fd677b40.
Report an issue: GitHub.