{"record":{"id":"afbfdb497b586867","repo":"nautechsystems/nautilus_trader","slug":"index-is-within-the-data-batch","errorCode":null,"errorMessage":"index is within the data batch","messagePattern":"index is within the data batch","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/data_iterator.rs","lineNumber":368,"sourceCode":"            self.heap.is_empty()\n        }\n    }\n\n    fn rebuild_heap(&mut self) {\n        self.heap.clear();\n\n        // Determine if we're in single-stream mode\n        if self.streams.len() == 1 {\n            self.single_priority = self.streams.keys().next().copied();\n            return;\n        }\n        self.single_priority = None;\n\n        for (&priority, data) in &self.streams {\n            let idx = *self.indices.get(&priority).unwrap_or(&0);\n            if idx < data.len() {\n                self.heap.push(HeapEntry {\n                    key: replay_key(data.get(idx).expect(\"index is within the data batch\")),\n                    priority,\n                    index: idx,\n                });\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n\n    use nautilus_model::{\n        data::{\n            Bar, FundingRateUpdate, IndexPriceUpdate, InstrumentClose, InstrumentStatus,\n            MarkPriceUpdate, OptionGreeks, OrderBookDelta, OrderBookDeltas, OrderBookDepth10,\n            QuoteTick, TradeTick,\n            stubs::{","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/data_iterator.rs#L350-L386","documentation":"When (re)building the DataIterator heap, the code iterates each priority stream, reads the current per-priority index, and fetches data.get(idx) to seed the heap. The guard idx < data.len() runs immediately before the get, so expect(\"index is within the data batch\") can only fail on an internal logic bug where the index map and stream contents disagree.","triggerScenarios":"Only reachable if self.indices contains an index >= data.len() for a priority while the preceding if-check passes — i.e. a code defect or concurrent mutation of self.streams between the check and the get; not triggerable through normal public API use.","commonSituations":"Seen only with a corrupted or concurrently modified DataIterator, or a regression introduced by a code change. Not a user configuration issue.","solutions":["Do not mutate streams concurrently with heap construction","Create a minimal reproduction and file a bug with the NautilusTrader project","Upgrade to a version containing the fix if this is a known regression"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Ensure per-priority indices match stream lengths before building the iterator:\nassert!(streams.iter().all(|(p, d)| indices.get(p).map_or(true, |i| *i < d.len())));","typeGuard":"fn index_in_range(idx: usize, data: &[impl Sized]) -> bool { idx < data.len() }","tryCatchPattern":"// Not catchable in normal Rust; isolate with a minimal backtest repro and file a bug.","preventionTips":["Avoid concurrent mutation of streams/indices maps","Construct DataIterator once and reuse rather than rebuilding mid-iteration","Keep to supported public constructors"],"tags":["rust","panic","backtest","invariant","heap"],"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"}