{"record":{"id":"046fb50ef0e7b090","repo":"nautechsystems/nautilus_trader","slug":"failed-to-subtract-12-months","errorCode":null,"errorMessage":"Failed to subtract 12 months","messagePattern":"Failed to subtract 12 months","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/data/bar.rs","lineNumber":270,"sourceCode":"            }\n\n            start_time\n        }\n        BarAggregation::Month => {\n            // Set to the first day of the year\n            let now_civil = Offset::UTC.to_datetime(now);\n            let mut start_time = Offset::UTC\n                .to_timestamp(\n                    Date::new(now_civil.year(), 1, 1)\n                        .expect(\"valid year start date\")\n                        .at(0, 0, 0, 0),\n                )\n                .expect(\"valid UTC year start\");\n            start_time += origin_offset;\n\n            if now < start_time {\n                start_time =\n                    subtract_n_months(start_time, 12).expect(\"Failed to subtract 12 months\");\n            }\n\n            let months_step =\n                u32::try_from(step).expect(\"`step` exceeds u32 range for month arithmetic\");\n\n            while start_time <= now {\n                start_time =\n                    add_n_months(start_time, months_step).expect(\"Failed to add months in loop\");\n            }\n\n            start_time =\n                subtract_n_months(start_time, months_step).expect(\"Failed to subtract months_step\");\n            start_time\n        }\n        BarAggregation::Year => {\n            let step_i32 =\n                i32::try_from(step).expect(\"`step` exceeds i32 range for year arithmetic\");\n","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/data/bar.rs#L252-L288","documentation":"get_time_bar_start computes the aligned start of a Month-aggregated time bar. When the origin offset pushes the Jan-1 anchor into the future relative to `now`, it subtracts 12 months to step back one year; if that calendar operation fails (outside jiff's supported range), the expect panics with this message. It is an internal invariant: with a valid in-range timestamp this path should never fail.","triggerScenarios":"Calling get_time_bar_start (directly or via start_timer_internal when registering a time bar aggregation timer) with a Month bar whose Jan-1 + origin anchor lands after `now` AND whose timestamp is near jiff's representable bounds (-9999..9999), so subtract_n_months(start_time, 12) returns Err.","commonSituations":"Feeding an extreme or corrupted Timestamp (negative nanoseconds near the era bounds, misconfigured clock, bad data backfill) into bar aggregation; a large time_bars_origin offset combined with a boundary-year timestamp; custom tooling that constructs Timestamps outside normal trading ranges.","solutions":["Check the `now` Timestamp passed to get_time_bar_start / the timer registration for out-of-range values; clamp to a realistic range (e.g. 1970-2100).","Reduce the time_bars_origin offset in the BarType specification so the anchor stays within range.","If you legitimately need boundary-era dates, upgrade the calendar arithmetic to checked fallible handling or use a wider date library.","File an issue with the exact timestamp and bar_type if this reproduces with ordinary dates."],"exampleFix":"// before\nget_time_bar_start(now, &bar_type, origin)\n// after\nlet clamped = now.clamp(MIN_TS, MAX_TS); // keep within sane range before computing bar start\nget_time_bar_start(clamped, &bar_type, origin)","handlingStrategy":"validation","validationCode":"let lo = Timestamp::from_unix_nanos(-62_167_219_200_000_000_000); // year 0\nlet hi = Timestamp::from_unix_nanos(253_402_300_799_000_000_000); // year 9999\nif now < lo || now > hi { return Err(\"timestamp outside calendar-supported range\"); }\nlet start = get_time_bar_start(now, &bar_type, origin);","typeGuard":"fn in_calendar_range(ts: Timestamp) -> bool {\n    ts.as_unix_nanos() >= -62_167_219_200_000_000_000\n        && ts.as_unix_nanos() <= 253_402_300_799_000_000_000\n}","tryCatchPattern":"let start = std::panic::catch_unwind(|| get_time_bar_start(now, &bar_type, origin))\n    .map_err(|_| anyhow::anyhow!(\"bar start computation failed for {bar_type}\"))?;","preventionTips":["Validate timestamps at system boundaries (clock sync, data ingestion) before bar aggregation.","Keep time_bars_origin offsets small and validated in configuration.","Test timer registration with boundary timestamps in CI."],"tags":["rust","panic","calendar-arithmetic","timestamp"],"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-14T05:17:10.506Z"}