{"record":{"id":"0e21bd6bc9df40b6","repo":"nautechsystems/nautilus_trader","slug":"empty-asks-array-for-instrument-id","errorCode":null,"errorMessage":"Empty asks array for {instrument_id}","messagePattern":"Empty asks array for (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/adapters/okx/src/websocket/parse.rs","lineNumber":1043,"sourceCode":"///\n/// # Errors\n///\n/// Returns an error if any quote levels contain values that cannot be parsed.\npub fn parse_quote_msg(\n    msg: &OKXBookMsg,\n    instrument_id: InstrumentId,\n    price_precision: u8,\n    size_precision: u8,\n    ts_init: UnixNanos,\n) -> anyhow::Result<QuoteTick> {\n    let best_bid: &OrderBookEntry = msg\n        .bids\n        .first()\n        .ok_or_else(|| anyhow::anyhow!(\"Empty bids array for {instrument_id}\"))?;\n    let best_ask: &OrderBookEntry = msg\n        .asks\n        .first()\n        .ok_or_else(|| anyhow::anyhow!(\"Empty asks array for {instrument_id}\"))?;\n\n    let bid_price = parse_price(&best_bid.price, price_precision)?;\n    let ask_price = parse_price(&best_ask.price, price_precision)?;\n    let bid_size = parse_quantity(&best_bid.size, size_precision)?;\n    let ask_size = parse_quantity(&best_ask.size, size_precision)?;\n    let ts_event = parse_millisecond_timestamp(msg.ts);\n\n    QuoteTick::new_checked(\n        instrument_id,\n        bid_price,\n        ask_price,\n        bid_size,\n        ask_size,\n        ts_event,\n        ts_init,\n    )\n}\n","sourceCodeStart":1025,"sourceCodeEnd":1061,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/websocket/parse.rs#L1025-L1061","documentation":"OKX quote (BBO ticker) messages must contain at least one bid and one ask to build a QuoteTick. This error is thrown in parse_quote_msg when msg.asks.first() is None, i.e. the exchange delivered a tick with no ask entries. The parser treats a one-sided or empty book snapshot as unparseable rather than emitting a partial quote.","triggerScenarios":"A WebSocket 'bbo-tbt' / quote tickers message arrives whose 'asks' array is empty (e.g. the matching engine temporarily has no resting asks, or an unusually shaped update frame arrives for an illiquid instrument). Raised in parse_quote_msg, called from parse_quote_msg_vec.","commonSituations":"Illiquid or newly listed instruments with one-sided books; deep-in-the-money far-dated contracts; OKX sending a partial book state during volatile sessions; subscribing to books channel with a depth where one side is momentarily empty.","solutions":["Skip quotes with empty asks before parsing: filter out msgs where msg.asks.is_empty() in the vec wrapper, keeping the last known quote as fallback","Check the channel subscription: use 'books'/'bbo-tbt' with full snapshot semantics and handle the initial snapshot whose one side may be empty","Log the instrument_id and skip/continue instead of failing the whole batch in parse_quote_msg_vec","Verify instrument is actively traded; unsubscribe from instruments whose book is persistently one-sided"],"exampleFix":"// before\nlet best_ask: &OrderBookEntry = msg\n    .asks\n    .first()\n    .ok_or_else(|| anyhow::anyhow!(\"Empty asks array for {instrument_id}\"))?;\n// after\nlet Some(best_ask) = msg.asks.first() else {\n    log::debug(\"Skipping quote for {instrument_id}: empty asks array\");\n    return Ok(None); // or continue to next message\n};","handlingStrategy":"validation","validationCode":"// Rust, before calling the quote parse path\nif msg.bids.is_empty() || msg.asks.is_empty() {\n    log::debug(\"Skipping quote for {}: one-sided/empty book\", msg.inst_id);\n    return Ok(None);\n}","typeGuard":"fn has_two_sided_book(bids: &[OrderBookEntry], asks: &[OrderBookEntry]) -> bool {\n    !bids.is_empty() && !asks.is_empty()\n}","tryCatchPattern":"match parse_quote_msg(&msg, instrument_id, price_precision, size_precision, ts_init) {\n    Ok(quote) => emit(quote),\n    Err(e) if e.to_string().contains(\"Empty asks array\") || e.to_string().contains(\"Empty bids array\") => {\n        log::debug(\"Skipping one-sided quote: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Filter empty-side messages in the vec wrapper before individual parsing","Keep a last-known-good quote cache to bridge one-sided updates","Monitor instruments with persistently one-sided books and unsubscribe","Pin and track OKX books/bbo-tbt channel semantics for snapshot vs update frames"],"tags":["okx","websocket","parsing","market-data","empty-array"],"backgroundTag":"empty-required-field","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}