nautechsystems/nautilus_trader · error

invalid negative order-book update ID

Error message

invalid negative order-book update ID

What it means

Thrown in `parse_book_snapshot_response` when the depth snapshot's `last_update_id` is negative and cannot be converted to the unsigned `u64` sequence number required by NautilusTrader's `OrderBook`. Binance should never send a negative update ID, so this is a defensive invariant check against malformed API responses.

Source

Thrown at crates/adapters/binance/src/spot/http/client.rs:3064

        }
        let instrument = self.instrument_from_cache_by_id(instrument_id)?;
        let params = DepthParams {
            symbol: instrument_id.symbol.to_string(),
            limit: depth,
        };
        let snapshot = self.inner.depth(&params).await?;
        let ts_event = self.generate_ts_init();
        Self::parse_book_snapshot_response(instrument_id, &instrument, &snapshot, ts_event)
    }

    fn parse_book_snapshot_response(
        instrument_id: InstrumentId,
        instrument: &InstrumentAny,
        snapshot: &BinanceDepth,
        ts_event: UnixNanos,
    ) -> anyhow::Result<OrderBook> {
        let sequence = u64::try_from(snapshot.last_update_id)
            .map_err(|_| anyhow::anyhow!("invalid negative order-book update ID"))?;
        let mut book = OrderBook::new(instrument_id, BookType::L2_MBP);
        let mut add_level = |level: &super::models::BinancePriceLevel,
                             side: OrderSide,
                             order_id: usize,
                             name: &str|
         -> anyhow::Result<()> {
            let price = Price::from_mantissa_exponent_checked(
                level.price_mantissa,
                snapshot.price_exponent,
                instrument.price_precision(),
            )
            .map_err(|e| anyhow::anyhow!("invalid {name} price: {e}"))?;
            anyhow::ensure!(price.is_positive(), "invalid non-positive {name} price");
            let qty_mantissa = u64::try_from(level.qty_mantissa)
                .map_err(|_| anyhow::anyhow!("invalid negative {name} quantity"))?;
            let quantity = Quantity::from_mantissa_exponent_checked(
                qty_mantissa,
                snapshot.qty_exponent,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the raw depth response to confirm `lastUpdateId` is a non-negative integer.
  2. Re-fetch the snapshot; a single malformed response is usually transient.
  3. If using a proxy/mock, fix the fixture to emit valid non-negative update IDs.
  4. Report a bug if a genuine Binance response contains a negative ID.

Example fix

// before: snapshot from an untrusted source used directly
let book = client.request_book_snapshot(instrument_id, Some(5000)).await?;
// after: retry on malformed snapshots
let book = match client.request_book_snapshot(instrument_id, Some(5000)).await {
    Ok(b) => b,
    Err(e) if e.to_string().contains("invalid negative order-book update ID") => {
        client.request_book_snapshot(instrument_id, Some(5000)).await?
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: validation

Validate before calling

fn has_valid_update_id(snapshot: &BinanceDepth) -> bool { snapshot.last_update_id >= 0 }

Type guard

fn valid_snapshot(s: &BinanceDepth) -> bool { s.last_update_id >= 0 && !s.bids.is_empty() && !s.asks.is_empty() }

Try / catch

match client.request_book_snapshot(id, depth).await {
    Ok(book) => book,
    Err(e) if e.to_string().contains("invalid negative order-book update ID") => retry_snapshot(id, depth).await?,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `request_book_snapshot` when the Binance `/api/v3/depth` response contains a negative `lastUpdateId` — essentially only possible with a corrupted, mocked, or non-conformant response payload.

Common situations: Proxy or MITM tooling returning synthesized/garbage depth JSON; a test fixture or mock server with negative IDs; a breaking change in the upstream API response schema.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/cf8dbb111dacf50a. Report an issue: GitHub.