{"record":{"id":"547e0c21297ce3bb","repo":"nautechsystems/nautilus_trader","slug":"invalid-negative-order-book-update-id","errorCode":null,"errorMessage":"invalid negative order-book update ID","messagePattern":"invalid negative order-book update ID","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/binance/src/futures/http/client.rs","lineNumber":3073,"sourceCode":"        instrument_id: InstrumentId,\n        depth: Option<u32>,\n    ) -> anyhow::Result<OrderBook> {\n        if depth.is_some_and(|value| !crate::common::consts::BINANCE_BOOK_DEPTHS.contains(&value)) {\n            anyhow::bail!(\n                \"invalid Binance Futures order-book depth; valid values are {:?}\",\n                crate::common::consts::BINANCE_BOOK_DEPTHS\n            );\n        }\n        let (symbol, price_precision, size_precision) =\n            self.cached_precisions_by_id(instrument_id)?;\n        let params = BinanceDepthParams {\n            symbol,\n            limit: depth,\n        };\n        let snapshot = self.inner.depth(&params).await?;\n        let ts_event = self.clock.get_time_ns();\n        let sequence = u64::try_from(snapshot.last_update_id)\n            .map_err(|_| anyhow::anyhow!(\"invalid negative order-book update ID\"))?;\n        let mut book = OrderBook::new(instrument_id, BookType::L2_MBP);\n        for (index, level) in snapshot.bids.iter().enumerate() {\n            let order = BookOrder::new(\n                OrderSide::Buy,\n                parse_required_price_at_precision(&level.0, price_precision, \"bid price\")?,\n                parse_required_quantity_at_precision(&level.1, size_precision, \"bid quantity\")?,\n                index as u64,\n            );\n            book.add(order, 0, sequence, ts_event);\n        }\n        let bid_count = snapshot.bids.len();\n        for (index, level) in snapshot.asks.iter().enumerate() {\n            let order = BookOrder::new(\n                OrderSide::Sell,\n                parse_required_price_at_precision(&level.0, price_precision, \"ask price\")?,\n                parse_required_quantity_at_precision(&level.1, size_precision, \"ask quantity\")?,\n                (bid_count + index) as u64,\n            );","sourceCodeStart":3055,"sourceCodeEnd":3091,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/binance/src/futures/http/client.rs#L3055-L3091","documentation":"Thrown by request_book_snapshot when snapshot.last_update_id is negative and therefore cannot convert to u64. The depth response's last_update_id is the book sequence number and must be a non-negative integer by protocol; a negative value means the response was malformed or deserialization mangled the field, and NautilusTrader refuses to construct an OrderBook with a bogus sequence rather than corrupting subsequent delta matching.","triggerScenarios":"A Binance depth response (or a mocked/replayed one in tests) where last_update_id is negative; proxy/fixture tooling that substitutes dummy negative values; a serde type change causing an offset field to land in last_update_id; effectively unreachable against the real venue and therefore almost always a test-harness or deserialization defect when seen.","commonSituations":"Unit tests with hand-written depth fixtures using placeholder IDs like -1; replay harnesses with synthesized snapshots; schema drift between the Binance response model and the venue's actual JSON after an API revision.","solutions":["If seen in tests: fix the fixture so last_update_id is a realistic non-negative u64","If seen live: log the raw response body and check for a proxy or gateway mutating payloads; verify the adapter version matches the current Binance API schema","Treat occurrence as a data-integrity alarm — halt book processing for the instrument rather than resynchronizing on the bad sequence"],"exampleFix":"// before (test fixture)\nlet snapshot = BinanceDepthResponse { last_update_id: -1, bids: vec![], asks: vec![] };\n\n// after\nlet snapshot = BinanceDepthResponse { last_update_id: 42, bids: vec![], asks: vec![] };","handlingStrategy":"try-catch","validationCode":"if snapshot.last_update_id < 0 {\n    log::error!(\"malformed depth payload: negative last_update_id={}\", snapshot.last_update_id);\n    // reject payload before building the book\n}","typeGuard":null,"tryCatchPattern":"Treat as a data-integrity failure: drop the snapshot, log the raw payload, and re-request the depth snapshot; do not resynchronize an existing book onto a bogus sequence.","preventionTips":["Use realistic non-negative IDs in depth fixtures","Never feed placeholder negative values through response models","Halt book processing for the instrument on malformed sequences"],"tags":["binance","futures","order-book","data-integrity","rust","nautilustrader"],"backgroundTag":"invalid-venue-response-data","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}