nautechsystems/nautilus_trader · error
Bybit order book update missing bid levels and no previous q
Error message
Bybit order book update missing bid levels and no previous quote provided
What it means
When building a `QuoteTick` from a Bybit order book message, no best bid could be extracted from `data.b` and no `last_quote` was supplied to fall back on. The adapter cannot produce a two-sided top-of-book quote, so it errors instead of emitting a one-sided quote.
Source
Thrown at crates/adapters/bybit/src/websocket/parse.rs:347
let size_precision = instrument.size_precision();
let get_best =
|levels: &[Vec<String>], label: &str| -> anyhow::Result<Option<(Price, Quantity)>> {
if let Some(values) = levels.first() {
parse_book_level(values, price_precision, size_precision, label).map(Some)
} else {
Ok(None)
}
};
let bids = get_best(&msg.data.b, "bid")?;
let asks = get_best(&msg.data.a, "ask")?;
let (bid_price, bid_size) = match (bids, last_quote) {
(Some(level), _) => level,
(None, Some(prev)) => (prev.bid_price, prev.bid_size),
(None, None) => {
anyhow::bail!(
"Bybit order book update missing bid levels and no previous quote provided"
);
}
};
let (ask_price, ask_size) = match (asks, last_quote) {
(Some(level), _) => level,
(None, Some(prev)) => (prev.ask_price, prev.ask_size),
(None, None) => {
anyhow::bail!(
"Bybit order book update missing ask levels and no previous quote provided"
);
}
};
QuoteTick::new_checked(
instrument.id(),
bid_price,View on GitHub (pinned to 18893faf8b)
Solutions
- Always cache the last parsed quote per symbol and pass it as `last_quote` so deltas can fall back to the previous bid.
- Wait for the snapshot message (`orderbook.1.<symbol>` with full levels) before processing delta messages.
- Skip delta frames received before the first snapshot instead of parsing them.
Example fix
// before: feed deltas with no prior state let quote = parse_orderbook_quote(&msg, None)?; // after: maintain per-symbol last quote let quote = parse_orderbook_quote(&msg, last_quotes.get(&symbol))?; last_quotes.insert(symbol, quote.clone());
Defensive patterns
Strategy: fallback
Validate before calling
let has_bid = msg.data.b.iter().any(|l| !l.is_empty());
let has_prev = last_quote.is_some();
if !has_bid && !has_prev {
return; // wait for snapshot
} Try / catch
match parse_orderbook_quote(&msg, last_quote) {
Ok(quote) => publish(quote),
Err(e) if e.to_string().contains("missing bid levels") => {
tracing::debug!("delta before snapshot; waiting for snapshot");
}
Err(e) => return Err(e),
} Prevention
- Cache the last parsed quote per symbol and pass it as `last_quote`
- Process the order book snapshot before any delta
- Drop deltas that arrive before the initial snapshot
- Re-seed cached quotes after WS reconnects
When it happens
Trigger: `parse_orderbook_quote` receives a delta update whose `b` list is empty (all bid levels deleted by the update) while `last_quote` is `None` — i.e. the first frame for a symbol is a delta with no snapshot and no cached prior quote.
Common situations: Starting a subscription and receiving a delta before the initial snapshot; reconnecting to the Bybit order book stream without re-seeding the cached last quote; a malformed message where the bid array is empty.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Bybit order book update missing ask levels and no previous q
- missing price component in {label} level
- missing size component in {label} level
- errors.join("; ")
- Bybit data shutdown failed: {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/790607daf5e1741f.
Report an issue: GitHub.