nautechsystems/nautilus_trader · error · anyhow::Error

missing size component in {label} level

Error message

missing size component in {label} level

What it means

parse_book_level expects a 2-element array [price, size] per orderbook level. This error means the price element existed but the size element (index 1) was missing, so the level cannot be converted into a Quantity. Thrown by the Bybit adapter while building orderbook deltas.

Source

Thrown at crates/adapters/bybit/src/common/parse.rs:964

        push_level(level, OrderSide::Sell)?;
    }

    OrderBookDeltas::new_checked(instrument_id, deltas)
        .context("failed to assemble OrderBookDeltas from Bybit message")
}

pub fn parse_book_level(
    level: &[String],
    price_precision: u8,
    size_precision: u8,
    label: &str,
) -> anyhow::Result<(Price, Quantity)> {
    let price_str = level
        .first()
        .ok_or_else(|| anyhow::anyhow!("missing price component in {label} level"))?;
    let size_str = level
        .get(1)
        .ok_or_else(|| anyhow::anyhow!("missing size component in {label} level"))?;
    let price = parse_price_with_precision(price_str, price_precision, label)?;
    let size = parse_quantity_with_precision(size_str, size_precision, label)?;
    Ok((price, size))
}

/// Parses a kline entry into a [`Bar`].
pub fn parse_kline_bar(
    kline: &BybitKline,
    instrument: &InstrumentAny,
    bar_type: BarType,
    timestamp_on_close: bool,
    ts_init: Option<UnixNanos>,
) -> anyhow::Result<Bar> {
    let price_precision = instrument.price_precision();
    let size_precision = instrument.size_precision();

    let open = parse_price_with_precision(&kline.open, price_precision, "kline.open")?;
    let high = parse_price_with_precision(&kline.high, price_precision, "kline.high")?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the raw frame and ensure each level contains both price and size elements.
  2. Validate level.len() == 2 in the caller before parsing; skip/log malformed entries.
  3. Update the adapter or Bybit API version alignment if the message schema changed.

Example fix

// before
let (p, s) = parse_book_level(level, price_precision, size_precision, "bid")?;
// after
if level.len() != 2 { anyhow::bail!("expected [price,size], got {level:?}"); }
let (p, s) = parse_book_level(level, price_precision, size_precision, "bid")?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: skip levels lacking a size component
if level.len() != 2 { log::warn!("malformed level: {level:?}"); continue; }

Type guard

fn has_price_and_size(level: &[serde_json::Value]) -> bool {
    level.len() == 2 && level.iter().all(|v| v.is_string())
}

Try / catch

let (price, size) = parse_book_level(level, pp, sp, label)
    .inspect_err(|e| log::warn!("dropping {label} level: {e:#}"))?;

Prevention

When it happens

Trigger: parse_orderbook passes a level array with exactly one element (level.get(1) returns None) to parse_book_level.

Common situations: Partial/truncated WebSocket updates; a custom transformer emitting only prices; schema drift from a Bybit API version change where size fields moved or were dropped.

Related errors


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