nautechsystems/nautilus_trader · error

Bybit order book update missing ask levels and no previous q

Error message

Bybit order book update missing ask levels and no previous quote provided

What it means

parse_orderbook_quote builds top-of-book quotes from delta updates; when the update carries no ask levels there is a fallback to the previous cached quote's ask side, and with no prior quote available a complete QuoteTick cannot be formed so parsing fails.

Source

Thrown at crates/adapters/bybit/src/websocket/parse.rs:357

    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,
        ask_price,
        bid_size,
        ask_size,
        ts_event,
        ts_init,
    )
    .context("failed to construct QuoteTick from Bybit order book message")
}

/// Parses a linear or inverse ticker payload into a [`QuoteTick`].

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass the previously parsed quote as `last_quote` for every order book update.
  2. Process the snapshot message first and only then apply deltas.
  3. Drop or queue delta frames that arrive before any snapshot for a symbol.

Example fix

// before
let quote = parse_orderbook_quote(&msg, None)?;
// after
let prev = book_state.get(&symbol).map(|q| q.as_quote());
let quote = parse_orderbook_quote(&msg, prev)?;
Defensive patterns

Strategy: fallback

Validate before calling

let has_ask = msg.data.a.iter().any(|l| !l.is_empty());
let has_prev = last_quote.is_some();
if !has_ask && !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 ask levels") => {
        tracing::debug!("delta before snapshot; waiting for snapshot");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `parse_orderbook_quote` gets a delta update whose `a` list is empty (all ask levels deleted) with `last_quote == None`, typically the first frame seen for a symbol.

Common situations: Processing order book deltas before the initial snapshot arrives; reconnecting without restoring the cached quote; a corrupt/empty ask array in the WS payload.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/6263696a6c7320fd. Report an issue: GitHub.