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
- Skip/ignore the message: treat one-sided quotes as no-data rather than errors at the call site.
- Subscribe to the depth/book channel as a fallback source for best bid/ask on illiquid instruments.
- 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
- Expect one-sided quotes on illiquid/newly listed instruments and skip them gracefully.
- Use depth/book data as a fallback for best bid/ask when quotes are one-sided.
- Do not treat empty-side quotes as fatal; they are normal during volatile or auction phases.
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
- Empty asks array for {instrument_id}
- Unknown position currency '{pos_ccy}' for instrument {instru
- Invalid timeframe for `BarSpecification`, was {timeframe}
- OKX only supports L2_MBP order book deltas
- Unsupported algo order type: {:?}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/154d34634caefcf5.
Report an issue: GitHub.