{"record":{"id":"34125e12926bd427","repo":"nautechsystems/nautilus_trader","slug":"e-34125e","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/data/bar.rs","lineNumber":358,"sourceCode":"    // Floor to start of day\n    let day_start = Offset::UTC\n        .to_timestamp(Offset::UTC.to_datetime(now).date().at(0, 0, 0, 0))\n        .expect(\"valid UTC day start\");\n    let base_time = day_start + daily_time_origin;\n\n    let time_difference = base_time.duration_until(now);\n    let period_ns = period.as_nanos();\n    debug_assert_ne!(period_ns, 0, \"bar period must be non-zero\");\n\n    // Use div_euclid for floor division (rounds toward -inf, not zero)\n    // so negative deltas (now before origin) yield the previous period boundary\n    let num_periods = time_difference.as_nanos().div_euclid(period_ns);\n\n    base_time + SignedDuration::from_nanos_i128(num_periods * period_ns)\n}\n\nfn duration_days(days: i64) -> SignedDuration {\n    try_duration_days(days).unwrap_or_else(|e| panic!(\"{e}\"))\n}\n\nfn try_duration_days(days: i64) -> anyhow::Result<SignedDuration> {\n    let hours = days\n        .checked_mul(24)\n        .ok_or_else(|| anyhow::anyhow!(\"days overflow i64 hours\"))?;\n    SignedDuration::try_from_hours(hours)\n        .ok_or_else(|| anyhow::anyhow!(\"days exceed signed duration range\"))\n}\n\nfn try_time_interval(step: usize, aggregation: BarAggregation) -> anyhow::Result<SignedDuration> {\n    let step_i64 = i64::try_from(step)\n        .map_err(|_| invalid_interval_step(step, aggregation, \"step exceeds i64 range\"))?;\n\n    let duration = match aggregation {\n        BarAggregation::Millisecond => SignedDuration::from_millis(step_i64),\n        BarAggregation::Second => SignedDuration::from_secs(step_i64),\n        BarAggregation::Minute => SignedDuration::try_from_mins(step_i64).ok_or_else(|| {","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/model/src/data/bar.rs#L340-L376","documentation":"This panic fires inside `duration_days`, which converts a day count into a `SignedDuration` for time-bar aggregation. The underlying `try_duration_days` fails in two ways: `days * 24` overflows i64 ('days overflow i64 hours'), or the hour count is outside the representable duration range ('days exceed signed duration range'). It is reached from `BarSpecification` step handling for Day/Week/Month/Year aggregations (bar.rs:180-190 and 577-587), so the step in the bar type is far too large.","triggerScenarios":"Calling interval/timedelta handling on a bar specification whose aggregation is Day, Week, Month, or Year with an extreme step: e.g. Year where step*365 days overflows the hours-to-duration conversion, or Day with step > i64::MAX/24 overflowing the `checked_mul(24)`. Also reachable via `find_closest_smaller_time` with such a period.","commonSituations":"A step typo with extra zeros, a step generated from config with confused units (seconds or milliseconds supplied where a day/week/month/year count is expected), or BarType strings assembled from external data. The magnitudes needed are astronomical, so in practice this means a units or data-quality bug, not a legitimate interval.","solutions":["Reduce the BarSpecification step so step x multiplier (1/7/30/365) days stays within a few thousand years at most.","Check the unit of the step value: for Day/Week/Month/Year aggregations it is a count of those units, not seconds or milliseconds.","If the step comes from external input, validate its magnitude before constructing the BarType; the validated path reports 'Invalid step in bar_type.spec.step' with the aggregation named instead of panicking.","Find the offending BarType string in your config/logs and fix it at the source."],"exampleFix":"// before: aggregation YEAR with an astronomical step count\nlet spec = BarSpecification::new(300_000_000, BarAggregation::Year, PriceType::Last);\nlet interval = spec.timedelta(); // panics: 'days exceed signed duration range'\n\n// after: bound calendar-aggregation steps before constructing the bar type\nfn step_ok(step: usize, agg: BarAggregation) -> bool {\n    let days = step.saturating_mul(match agg {\n        BarAggregation::Day => 1,\n        BarAggregation::Week => 7,\n        BarAggregation::Month => 30,\n        BarAggregation::Year => 365,\n        _ => return true,\n    });\n    days.checked_mul(24).map(|h| h <= 2_500_000_000_000_000).unwrap_or(false)\n}","handlingStrategy":"validation","validationCode":"fn calendar_step_ok(step: usize, agg: BarAggregation) -> bool {\n    let days = step.saturating_mul(match agg {\n        BarAggregation::Day => 1,\n        BarAggregation::Week => 7,\n        BarAggregation::Month => 30,\n        BarAggregation::Year => 365,\n        _ => return true,\n    });\n    days.checked_mul(24).map(|h| h <= 2_500_000_000_000_000).unwrap_or(false)\n}\n// call before constructing the BarType","typeGuard":null,"tryCatchPattern":"Prefer validated construction (the builder reports 'Invalid step in bar_type.spec.step' by name). As a last-resort boundary: std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| spec.timedelta())) and treat Err(payload) as 'reject this bar type'. From Python, panics surface as pyo3_runtime.PanicException — catch it and drop the invalid spec.","preventionTips":["Bound step values for Day/Week/Month/Year aggregations at config load.","Confirm step units (counts of days/weeks/months/years, never seconds).","Add a smoke test that constructs every configured BarType."],"tags":["rust","nautilustrader","bar-aggregation","panic","integer-overflow"],"backgroundTag":"duration-overflow","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}