nautechsystems/nautilus_trader · error
Empty order book: no valid price levels for market order
Error message
Empty order book: no valid price levels for market order
What it means
After parsing book levels, calculate_market_price drops levels with zero price or zero size; if nothing valid remains it bails out, since a book whose every level is degenerate offers no tradable price. This differs from the fully-empty-book case: levels were present but none were usable.
Source
Thrown at crates/adapters/polymarket/src/execution/parse.rs:774
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)),
}
let mut remaining = amount;
let mut last_price = Decimal::ZERO;
let mut total_base_qty = Decimal::ZERO;
for &(price, size) in &parsed_levels {
last_price = price;
match side {
PolymarketOrderSide::Buy => {
let level_usdc = size
.checked_mul(price)View on GitHub (pinned to 18893faf8b)
Solutions
- Filter or sanitize book levels before calling, and treat an all-degenerate book as 'no liquidity'.
- Re-fetch the book from the CLOB API; an all-zero snapshot is usually transient or malformed.
- Log the raw payload when this fires to confirm whether the adapter or the exchange produced the bad data.
- Check adapter/API version compatibility in case the book schema changed and fields now deserialize as zero.
Example fix
// before let mp = calculate_market_price(&book.levels, amount, side)?; // after let valid: Vec<_> = book.levels.iter().filter(|l| !l.size.is_zero() && !l.price.is_zero()).collect(); anyhow::ensure!(!valid.is_empty(), "book has no tradable levels; skipping"); let mp = calculate_market_price(&book.levels, amount, side)?;
Defensive patterns
Strategy: validation
Validate before calling
let usable = book_levels.iter()
.any(|l| !l.price.is_zero() && !l.size.is_zero());
if !usable {
return Err("book contains only zero price/size levels".into());
} Prevention
- Sanitize or drop zero-sized/zero-priced levels before pricing
- Log raw book payloads when all levels are degenerate
- Pin the expected book schema in deserialization tests
- Treat an all-zero book as no-liquidity, equivalent to an empty book
When it happens
Trigger: Calling calculate_market_price with a non-empty book_levels slice where every level has price == 0 or size == 0 — typically from a malformed or placeholder API snapshot (all-zero payload) rather than a genuinely empty array.
Common situations: Upstream exchange returning zero-filled book rows during incidents; deserialization of a stub/placeholder book; stale cached snapshots zeroed out by a serializer bug.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Empty order book: no liquidity available for market order
- market-book price must be in (0, 1)
- Unsupported `TimeInForce` for Polymarket market order: {valu
- Polymarket only supports L2_MBP order book deltas, received
- Polymarket does not support OrderBookDepth10 subscriptions;
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/6f2b28efb43c0981.
Report an issue: GitHub.