nautechsystems/nautilus_trader · error
invalid non-positive {name} quantity
Error message
invalid non-positive {name} quantity What it means
Sanity guard when parsing Binance order-book snapshot levels: after decoding, each level's quantity must be a positive value; a zero or negative quantity indicates malformed venue data and is rejected (with the level name in the message).
Source
Thrown at crates/adapters/binance/src/spot/http/client.rs:3086
order_id: usize,
name: &str|
-> anyhow::Result<()> {
let price = Price::from_mantissa_exponent_checked(
level.price_mantissa,
snapshot.price_exponent,
instrument.price_precision(),
)
.map_err(|e| anyhow::anyhow!("invalid {name} price: {e}"))?;
anyhow::ensure!(price.is_positive(), "invalid non-positive {name} price");
let qty_mantissa = u64::try_from(level.qty_mantissa)
.map_err(|_| anyhow::anyhow!("invalid negative {name} quantity"))?;
let quantity = Quantity::from_mantissa_exponent_checked(
qty_mantissa,
snapshot.qty_exponent,
instrument.size_precision(),
)
.map_err(|e| anyhow::anyhow!("invalid {name} quantity: {e}"))?;
anyhow::ensure!(
quantity.is_positive(),
"invalid non-positive {name} quantity"
);
let order = BookOrder::new(
side,
price,
quantity,
u64::try_from(order_id)
.map_err(|_| anyhow::anyhow!("order-book level index overflow"))?,
);
book.add(order, 0, sequence, ts_event);
Ok(())
};
for (index, level) in snapshot.bids.iter().enumerate() {
add_level(level, OrderSide::Buy, index, "bid")?;
}
let bid_count = snapshot.bids.len();View on GitHub (pinned to 18893faf8b)
Solutions
- Refresh the instruments cache so `size_precision` matches the current LOT_SIZE step (a too-coarse precision can round tiny quantities to zero).
- Re-fetch the snapshot to rule out transient data issues.
- Inspect raw JSON levels to find the zero-quantity level.
- Report to maintainers if genuine exchange data triggers this.
Example fix
// before let book = client.request_book_snapshot(instrument_id, depth).await?; // after: refresh instrument precision first client.reload_instruments().await?; let book = client.request_book_snapshot(instrument_id, depth).await?;
Defensive patterns
Strategy: validation
Validate before calling
fn quantities_positive(snapshot: &BinanceDepth, precision: u8) -> bool {
snapshot.bids.iter().chain(snapshot.asks.iter()).all(|l| {
l.qty_mantissa as f64 * 10f64.powi(l.qty_mantissa.to_string().len() as i32) > 0.0 || l.qty_mantissa > 0
})
} Type guard
fn positive_qty_level(l: &BinancePriceLevel) -> bool { l.qty_mantissa > 0 } Try / catch
match client.request_book_snapshot(id, depth).await {
Ok(book) => book,
Err(e) if e.to_string().contains("non-positive") && e.to_string().contains("quantity") => {
reload_instruments_and_retry(id, depth).await?
}
Err(e) => return Err(e),
} Prevention
- Ensure instrument size_precision is not coarser than the exchange step, or dust quantities round to zero.
- Refresh instrument definitions before large snapshot requests.
- Filter zero-quantity levels in any custom pre-processing.
When it happens
Trigger: Calling `request_book_snapshot` when any depth level decodes to a zero quantity — e.g. a level that rounds to zero at the instrument's `size_precision`, or a malformed payload with 0 quantity.
Common situations: Very small quantities rounded to zero at coarse size precision (stale instrument definitions); mock/fixture data with zero quantities; corrupted upstream responses.
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
- invalid negative {name} quantity
- invalid {name} quantity: {e}
- Binance Spot order-book depth must be between 1 and 5000
- Binance Spot L1_MBP supports depth 1 only
- invalid negative order-book update ID
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/29961e3d85c3ee14.
Report an issue: GitHub.