nautechsystems/nautilus_trader · warning
empty Lighter WebSocket order book update
Error message
empty Lighter WebSocket order book update
What it means
parse_ws_order_book_deltas rejects order book WebSocket updates that contain no bid or ask levels (unless the update is a snapshot). The adapter treats an empty incremental update as a protocol/data anomaly rather than emitting an empty deltas batch.
Source
Thrown at crates/adapters/lighter/src/websocket/parse.rs:118
/// `is_snapshot` must be supplied by the caller because Lighter sends
/// `subscribed/order_book` for the full book on subscription and
/// `update/order_book` for incremental level changes afterwards.
///
/// # Errors
///
/// Returns an error if any price or size cannot be converted.
pub fn parse_ws_order_book_deltas(
book: &LighterWsOrderBook,
instrument: &InstrumentAny,
timestamp_ms: u64,
is_snapshot: bool,
ts_init: UnixNanos,
) -> anyhow::Result<OrderBookDeltas> {
let ts_event = parse_millis_to_nanos(timestamp_ms)?;
let sequence = u64::try_from(book.nonce).context("negative Lighter book nonce")?;
let total_levels = book.bids.len() + book.asks.len();
anyhow::ensure!(
is_snapshot || total_levels > 0,
"empty Lighter WebSocket order book update",
);
let mut deltas = Vec::with_capacity(total_levels + usize::from(is_snapshot));
if is_snapshot {
let mut clear = OrderBookDelta::clear(instrument.id(), sequence, ts_event, ts_init);
if total_levels == 0 {
clear.flags |= RecordFlag::F_LAST as u8;
}
deltas.push(clear);
}
let mut processed = 0_usize;
for bid in &book.bids {
processed += 1;View on GitHub (pinned to 18893faf8b)
Solutions
- Skip/ignore empty updates upstream if they are benign keep-alives
- Log the raw message and check whether the venue changed the update schema
- Handle the anyhow error by filtering it out in the message handler instead of failing the stream
- Verify subscription is still valid and the market is active
Defensive patterns
Strategy: try-catch
Validate before calling
if !is_snapshot && book.bids.is_empty() && book.asks.is_empty() {
return Ok(()); // skip empty update
} Type guard
fn has_levels(book: &WsBook) -> bool { !book.bids.is_empty() || !book.asks.is_empty() } Try / catch
match parse_ws_order_book_deltas(msg) {
Err(e) if e.to_string().contains("empty Lighter WebSocket order book update") => {
log::debug!("skipping empty book update");
}
other => other?,
} Prevention
- Treat empty updates as benign and skip them in the handler
- Monitor venue schema changes
- Check book subscription health if empty updates cluster
When it happens
Trigger: A book update message arrives with empty bids and asks arrays and is_snapshot is false, e.g. venue sends a keep-alive-like update or an unexpected payload shape.
Common situations: Venue protocol change or edge-case message (book emptied via separate delete events); network issues producing truncated payloads; subscribing to a book that has been delisted.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Book snapshot hash mismatch for {}: expected {expected}, com
- subscription state lock poisoned
- InstrumentState channel requires kind and currency parameter
- `handle_book_mbo` {IMPL_ERR} `{}`
- NoOrderSide
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/6b524c77cfba7bee.
Report an issue: GitHub.