nautechsystems/nautilus_trader · error · anyhow::Error

No order book returned from OKX

Error message

No order book returned from OKX

What it means

Response guard in the OKX HTTP client's order book fetch: the API returned an empty list of books, so no snapshot exists for the requested instrument/depth and the call fails instead of returning an empty book.

Source

Thrown at crates/adapters/okx/src/http/client.rs:2912

    ) -> anyhow::Result<OrderBook> {
        let inst = self.instrument_from_cache_by_id(instrument_id)?;
        let price_precision = inst.price_precision();
        let size_precision = inst.size_precision();

        let params = GetOrderBookParams {
            inst_id: instrument_id.symbol.to_string(),
            sz: depth,
        };

        let resp = self
            .inner
            .get_order_book(params)
            .await
            .map_err(|e| anyhow::anyhow!(e))?;

        let snapshot = resp
            .first()
            .ok_or_else(|| anyhow::anyhow!("No order book returned from OKX"))?;

        let ts_event = UnixNanos::from(snapshot.ts * NANOSECONDS_IN_MILLISECOND);
        let mut book = OrderBook::new(instrument_id, BookType::L2_MBP);

        for (i, level) in snapshot.bids.iter().enumerate() {
            let price = parse_price(&level.0, price_precision)?;
            let size = parse_quantity(&level.1, size_precision)?;
            let order = BookOrder::new(OrderSide::Buy, price, size, i as u64);
            book.add(order, 0, i as u64, ts_event);
        }

        let bids_len = snapshot.bids.len();

        for (i, level) in snapshot.asks.iter().enumerate() {
            let price = parse_price(&level.0, price_precision)?;
            let size = parse_quantity(&level.1, size_precision)?;
            let order = BookOrder::new(OrderSide::Sell, price, size, (bids_len + i) as u64);
            book.add(order, 0, (bids_len + i) as u64, ts_event);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the inst_id exists via /api/v5/public/instruments before requesting the book.
  2. Re-sync the instrument cache if the instrument may have been delisted.
  3. Treat empty responses as a skip-and-retry condition rather than a fatal error.
  4. Check OKX system status for ongoing incidents.

Example fix

// before
let snapshot = resp.first().ok_or_else(|| anyhow::anyhow!("No order book returned from OKX"))?;
// after
let Some(snapshot) = resp.first() else {
    tracing::warn!("OKX book snapshot empty for {instrument_id}; will retry");
    return Err(anyhow::anyhow!("empty order book response"));
};
Defensive patterns

Strategy: fallback

Validate before calling

if !okx_instruments_live.contains(inst_id.as_str()) {
    return Err(anyhow::anyhow!("instrument {inst_id} not live on OKX"));
}

Try / catch

let Some(snapshot) = resp.first() else {
    tracing::warn!("empty OKX book for {instrument_id}");
    return Err(anyhow::anyhow!("empty book response; retry later"));
};

Prevention

When it happens

Trigger: OKX responds with data: [] for get_order_book — typically an unknown/delisted instId or a race where the instrument was just suspended/delisted mid-request.

Common situations: Requesting a snapshot for an instrument removed from OKX, a typo'd or mis-derived inst_id, or exchange-side data gaps during incidents/maintenance.

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/e4e984310ebc0c96. Report an issue: GitHub.