nautechsystems/nautilus_trader · error
market-book size must be non-negative
Error message
market-book size must be non-negative
What it means
calculate_market_price requires every book level size to be non-negative. A negative size is impossible in a real order book and indicates corrupt venue data, so the level is rejected with this ensure! guard instead of corrupting the liquidity walk.
Source
Thrown at crates/adapters/polymarket/src/execution/parse.rs:763
amount: Decimal,
side: PolymarketOrderSide,
) -> anyhow::Result<MarketPriceResult> {
if book_levels.is_empty() {
anyhow::bail!("Empty order book: no liquidity available for market order");
}
// Parse and sort levels deterministically so we never depend on API ordering.
// BUY: asks ascending (best/lowest first). SELL: bids descending (best/highest first).
anyhow::ensure!(amount > Decimal::ZERO, "market amount must be positive");
let mut parsed_levels = Vec::with_capacity(book_levels.len());
for level in book_levels {
let price = parse_decimal_exact(&level.price).context("invalid market-book price")?;
let size = parse_decimal_exact(&level.size).context("invalid market-book size")?;
anyhow::ensure!(
price > Decimal::ZERO && price < Decimal::ONE,
InvalidMarketPriceError("market-book price must be in (0, 1)".to_string())
);
anyhow::ensure!(
size >= Decimal::ZERO,
"market-book size must be non-negative"
);
if !size.is_zero() {
parsed_levels.push((price, size));
}
}
if parsed_levels.is_empty() {
anyhow::bail!("Empty order book: no valid price levels for market order");
}
match side {
PolymarketOrderSide::Buy => parsed_levels.sort_by_key(|a| a.0),
PolymarketOrderSide::Sell => parsed_levels.sort_by_key(|b| std::cmp::Reverse(b.0)),
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Dump the raw book response and find the level with the negative size to locate the corruption source.
- Fix the book maintenance code so level updates/deletions can never leave negative sizes.
- Skip or drop non-negative-violating levels and rebuild the snapshot from a fresh REST fetch.
Example fix
// before
levels.push(Level { size: delta_size, .. }); // delta can be negative
// after
let new_size = existing.size + delta_size;
anyhow::ensure!(new_size >= Decimal::ZERO);
levels.push(Level { size: new_size, .. }); Defensive patterns
Strategy: validation
Validate before calling
if levels.iter().any(|l| Decimal::from_str(&l.size).map(|s| s < Decimal::ZERO).unwrap_or(true)) {
return Err(anyhow!("book contains negative sizes"));
} Type guard
fn has_non_negative_sizes(levels: &[Level]) -> bool {
levels.iter().all(|l| Decimal::from_str(&l.size).map(|s| s >= Decimal::ZERO).unwrap_or(false))
} Prevention
- Rebuild book state from fresh snapshots after anomalies
- Never let delta application leave negative level sizes
- Validate API payloads before applying them to local state
When it happens
Trigger: A market-book response passed to calculate_market_price contains a level whose parsed size is negative — e.g. a malformed API payload, a diff/update applied in the wrong order, or a sign-convention bug in a local book builder.
Common situations: Applying book deltas without handling deletions, leaving negative residuals; buggy caching of book state; venue API incidents returning invalid payloads.
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 depth {depth} for Binance Futures order book. Valid
- invalid depth {depth}; supported depths: {DERIBIT_BOOK_VALID
- Book snapshot hash mismatch for {}: expected {expected}, com
- Empty book snapshot for {instrument_id}
- Polymarket collateral-sized limit BUY price must be positive
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f66d92bb42b0cfd0.
Report an issue: GitHub.