{"record":{"id":"2c934f93ea2f41da","repo":"nautechsystems/nautilus_trader","slug":"mbo-delta-tail-must-be-queued","errorCode":null,"errorMessage":"MBO delta tail must be queued","messagePattern":"MBO delta tail must be queued","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/databento/src/decode/market_data.rs","lineNumber":298,"sourceCode":"    }\n\n    fn release(&mut self, instrument_id: InstrumentId, flags: u8) {\n        let tail = match self.tail {\n            Some((tail_instrument_id, tail)) if tail_instrument_id == instrument_id => {\n                self.tail = None;\n                tail\n            }\n            _ => {\n                let Some(tail) = self.tails.remove(&instrument_id) else {\n                    return;\n                };\n                tail\n            }\n        };\n        let offset = tail\n            .checked_sub(self.head)\n            .and_then(|offset| usize::try_from(offset).ok())\n            .expect(\"MBO delta tail must be queued\");\n        let queued = self\n            .queue\n            .get_mut(offset)\n            .expect(\"MBO delta tail must be queued\");\n        queued.delta.flags |= flags;\n        queued.ready = true;\n    }\n}\n\n/// Decodes a Databento Trade message into a `TradeTick`.\n///\n/// # Errors\n///\n/// Returns an error if decoding the Trade message fails.\npub fn decode_trade_msg(\n    msg: &dbn::TradeMsg,\n    instrument_id: InstrumentId,\n    price_precision: u8,","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/databento/src/decode/market_data.rs#L280-L316","documentation":"`release` finalizes an incomplete MBO delta sequence by marking its tail element ready. It computes the tail's offset in the internal queue as `tail - head` and panics if the tail index is not representable (negative or overflowing usize), meaning the recorded tail is not actually in the current queue window.","triggerScenarios":"Calling `release` with a stored tail index that predates already-popped entries (tail < head), or an integer overflow in `tail - head` — i.e. the buffer's tail bookkeeping is out of sync with head.","commonSituations":"A decoder bug that pops deltas without clearing `self.tail`, or mixing buffer instances / reusing a buffer across messages where a stale tail from a previous session lingers. Also triggered by usize overflow on 32-bit platforms with huge queues.","solutions":["Check that `finish()`/`pop_ready()` clears `self.tail` when the queue drains, so stale tails never survive past their elements.","Recreate the decoder/buffer for the affected instrument to clear stale state.","Report upstream with the instrument_id and message sequence if reproducible; it indicates a bookkeeping bug in the buffer."],"exampleFix":"// before\nself.release(instrument_id, 0); // stale tail from earlier pops survives\n// after (in pop_ready/drain path)\nif self.queue.is_empty() {\n    self.tail = None;\n    self.head = self.next;\n}","handlingStrategy":"try-catch","validationCode":"// before streaming, ensure a fresh buffer per instrument\nlet buffer = MboDeltaBuffer::new();","typeGuard":null,"tryCatchPattern":"// panics are not catchable in Rust safely; wrap decode in a task and treat panic as feed failure\nlet result = std::panic::catch_unwind(|| decoder.handle(msg));\nif result.is_err() { reconnect_feed(); }","preventionTips":["Always call finish() when a subscription ends so stale tails are cleared.","Never share a buffer across instruments or sessions.","Ensure the is_last delta of every sequence is pushed before release."],"tags":["rust","panic","invariant","order-book"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}