nautechsystems/nautilus_trader · warning · anyhow::Error

Empty bids array for {instrument_id}

Error message

Empty bids array for {instrument_id}

What it means

When parsing an OKX quote (ticker) message into a QuoteTick, the adapter takes the first element of the bids (and asks) arrays as the best price. OKX occasionally emits quote updates with empty bid or ask sides (one-sided market or snapshot gap); since a QuoteTick requires both, the parser returns this error instead of producing a tick.

Source

Thrown at crates/adapters/okx/src/websocket/parse.rs:1039

    OrderBookDeltas::new_checked(instrument_id, deltas)
}

/// Parses an OKX book message into a Nautilus quote tick.
///
/// # Errors
///
/// Returns an error if any quote levels contain values that cannot be parsed.
pub fn parse_quote_msg(
    msg: &OKXBookMsg,
    instrument_id: InstrumentId,
    price_precision: u8,
    size_precision: u8,
    ts_init: UnixNanos,
) -> anyhow::Result<QuoteTick> {
    let best_bid: &OrderBookEntry = msg
        .bids
        .first()
        .ok_or_else(|| anyhow::anyhow!("Empty bids array for {instrument_id}"))?;
    let best_ask: &OrderBookEntry = msg
        .asks
        .first()
        .ok_or_else(|| anyhow::anyhow!("Empty asks array for {instrument_id}"))?;

    let bid_price = parse_price(&best_bid.price, price_precision)?;
    let ask_price = parse_price(&best_ask.price, price_precision)?;
    let bid_size = parse_quantity(&best_bid.size, size_precision)?;
    let ask_size = parse_quantity(&best_ask.size, size_precision)?;
    let ts_event = parse_millisecond_timestamp(msg.ts);

    QuoteTick::new_checked(
        instrument_id,
        bid_price,
        ask_price,
        bid_size,
        ask_size,
        ts_event,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Skip/ignore the message: treat one-sided quotes as no-data rather than errors at the call site.
  2. Subscribe to the depth/book channel as a fallback source for best bid/ask on illiquid instruments.
  3. Filter out instruments with persistently one-sided books if your strategy cannot handle missing quotes.

Example fix

// before
let quote = parse_quote_msg(msg, ...)?; // errors on empty bids
// after
match parse_quote_msg(msg, ...) {
    Ok(q) => handle(q),
    Err(e) if e.to_string().contains("Empty bids") => continue, // skip one-sided quote
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: fallback

Validate before calling

// rust
// pre-check if you control message handling
if msg.bids.is_empty() || msg.asks.is_empty() { skip_quote(); }

Type guard

// rust
fn has_two_sided_book(bids: &[OrderBookEntry], asks: &[OrderBookEntry]) -> bool {
    !bids.is_empty() && !asks.is_empty()
}

Try / catch

// rust
match parse_quote_msg(msg, ...) {
    Ok(q) => handle(q),
    Err(e) if e.to_string().starts_with("Empty bids") || e.to_string().starts_with("Empty asks") => skip_quote(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A quote message arrives whose `bids` array is empty (no best bid available) while parsing via parse_quote_msg; similarly for empty asks (see the sibling error).

Common situations: Illiquid or newly listed instruments with no resting bids; market open/auction phases; OKX sending one-sided book snapshots during volatile periods.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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