{"record":{"id":"33f746b769e847da","repo":"nautechsystems/nautilus_trader","slug":"deltas-instrument-ids-must-match-instrument-id","errorCode":null,"errorMessage":"`deltas` instrument IDs must match `instrument_id` {instrument_id}, but delta at index {index} of {} has {}","messagePattern":"`deltas` instrument IDs must match `instrument_id` (.+?), but delta at index (.+?) of (.+?) has (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/data/deltas.rs","lineNumber":105,"sourceCode":"        instrument_id: InstrumentId,\n        deltas: Vec<OrderBookDelta>,\n    ) -> anyhow::Result<Self> {\n        check_predicate_true(!deltas.is_empty(), \"`deltas` cannot be empty\")?;\n\n        let mismatch = deltas.iter().enumerate().find(|(_, delta)| {\n            instrument_id != delta.instrument_id\n                && (instrument_id.symbol.as_str() != delta.instrument_id.symbol.as_str()\n                    || instrument_id.venue.as_str() != delta.instrument_id.venue.as_str())\n        });\n\n        if let Some((index, delta)) = mismatch {\n            check_predicate_true(\n                false,\n                &format!(\n                    \"`deltas` instrument IDs must match `instrument_id` {instrument_id}, but \\\n                     delta at index {index} of {} has {}\",\n                    deltas.len(),\n                    delta.instrument_id,\n                ),\n            )?;\n        }\n        let last = deltas.last().expect(\"deltas not empty\");\n        let flags = last.flags;\n        let sequence = last.sequence;\n        let ts_event = last.ts_event;\n        let ts_init = last.ts_init;\n        Ok(Self {\n            instrument_id,\n            deltas,\n            flags,\n            sequence,\n            ts_event,\n            ts_init,\n        })\n    }\n","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/data/deltas.rs#L87-L123","documentation":"`timedelta`'s Year arm proxies a year as 365 days: `step.checked_mul(365)`. The `.expect(\"`step` overflows i64 days\")` panics when step * 365 exceeds i64::MAX. This mirrors the Month/Week proxy calculations and guards the duration conversion.","triggerScenarios":"Calling `timedelta` (via `bar_close_from_open`, `py_timedelta`, `py_get_interval_ns`) on a BarType with Year aggregation and step > i64::MAX / 365 (about 25 quadrillion years).","commonSituations":"Unit-conversion bugs when building the BarType step; corrupted or malicious deserialized specs; generated aggregation specs with runaway multipliers.","solutions":["Validate year step values are realistic before constructing the BarType","Audit where the step value is produced for unit mistakes","Re-validate BarType specs after deserialization"],"exampleFix":"// before\nlet td = bar_type.timedelta();\n// after\nassert!(bar_type.aggregation() != BarAggregation::Year || bar_type.step().get() < 25_000_000_000_000_000, \"year step too large\");\nlet td = bar_type.timedelta();","handlingStrategy":"validation","validationCode":"def validate_year_step(step: int) -> int:\n    if step >= 25_268_416_409_306:  # i64::MAX // 365 (approx)\n        raise ValueError(f\"year step {step} overflows i64 days\")\n    return step","typeGuard":null,"tryCatchPattern":"try:\n    td = bar_type.timedelta\nexcept Exception as e:\n    logger.error(f\"year step overflow: {e}\")\n    raise","preventionTips":["Validate year steps before constructing BarType","Check multiplier math in spec generators","Re-validate deserialized specs"],"tags":["rust","panic","overflow","bar-aggregation"],"backgroundTag":"value-out-of-range","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"}