nautechsystems/nautilus_trader · error · anyhow::Error
No RPI order book returned from OKX
Error message
No RPI order book returned from OKX
What it means
request_rpi_order_book_snapshot calls OKX's RPI (retail price improvement) order book endpoint and expects one snapshot; an empty data array triggers this error, mirroring the regular order book path but for the RPI book variant.
Source
Thrown at crates/adapters/okx/src/http/client.rs:2967
&self,
instrument_id: InstrumentId,
depth: Option<u32>,
) -> 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 = GetRpiOrderBookParams {
inst_id: instrument_id.symbol.to_string(),
sz: depth,
};
let resp = self
.inner
.get_rpi_order_book(params)
.await
.map_err(|e| anyhow::anyhow!(e))?;
let snapshot = resp
.first()
.ok_or_else(|| anyhow::anyhow!("No RPI 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 = Price::from_decimal_dp(level.0, price_precision)?;
let size = Quantity::from_decimal_dp(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 = Price::from_decimal_dp(level.0, price_precision)?;
let size = Quantity::from_decimal_dp(level.1, size_precision)?;
let index = (bids_len + i) as u64;
let order = BookOrder::new(OrderSide::Sell, price, size, index);
book.add(order, 0, index, ts_event);
}View on GitHub (pinned to 18893faf8b)
Solutions
- Confirm the instrument supports OKX's RPI order book before requesting it.
- Fall back to the standard order book endpoint when the RPI book is unavailable.
- Verify the inst_id is valid and currently trading.
- Handle the empty case gracefully (skip or retry) instead of surfacing it as fatal.
Example fix
// before
let snapshot = resp.first().ok_or_else(|| anyhow::anyhow!("No RPI order book returned from OKX"))?;
// after
let Some(snapshot) = resp.first() else {
tracing::debug!("no RPI book for {instrument_id}, falling back to standard book");
return self.request_book_snapshot(instrument_id, depth, ts_init).await;
}; Defensive patterns
Strategy: fallback
Validate before calling
if !supports_rpi(inst_type) {
return self.request_book_snapshot(instrument_id, depth, ts_init).await;
} Try / catch
let Some(snapshot) = resp.first() else {
tracing::debug!("no RPI book; falling back to standard order book");
return self.request_book_snapshot(instrument_id, depth, ts_init).await;
}; Prevention
- Only request the RPI book for instrument types that support OKX's RPI program.
- Provide a standard order book fallback path.
- Treat empty RPI responses as informational, not fatal.
When it happens
Trigger: Calling the RPI order book request for an instrument that has no RPI book data (unsupported instrument type, empty response from get_rpi_order_book), or an invalid instId.
Common situations: Requesting RPI books for instruments that don't support the RPI program, using an adapter/endpoint combination where OKX returns data: [] for the RPI depth endpoint, or polling a suspended instrument.
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
- No order book returned from OKX
- No usable instruments for {instrument_type:?}, cannot initia
- No usable instruments for {instrument_type:?} family {family
- No instruments loaded for configured types {instrument_types
- Failed to deserialize Books channel data as Vec<OKXBookMsg>
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/217d34f48c091ca5.
Report an issue: GitHub.