{"record":{"id":"23d085538191fa34","repo":"nautechsystems/nautilus_trader","slug":"durationnanos-overflow-in-from-hours","errorCode":null,"errorMessage":"DurationNanos overflow in from_hours","messagePattern":"DurationNanos overflow in from_hours","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/core/src/nanos.rs","lineNumber":196,"sourceCode":"    /// Creates a duration from a number of whole minutes.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the result exceeds [`DurationNanos::MAX`].\n    pub const fn try_from_mins(mins: u64) -> Result<Self, DurationNanosOutOfRangeError> {\n        Self::try_from_units(mins, NANOSECONDS_IN_MINUTE, \"minutes\")\n    }\n\n    /// Creates a duration from a number of whole hours.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the result exceeds [`DurationNanos::MAX`].\n    #[must_use]\n    pub const fn from_hours(hours: u64) -> Self {\n        match Self::try_from_hours(hours) {\n            Ok(duration) => duration,\n            Err(_) => panic!(\"DurationNanos overflow in from_hours\"),\n        }\n    }\n\n    /// Creates a duration from a number of whole hours.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the result exceeds [`DurationNanos::MAX`].\n    pub const fn try_from_hours(hours: u64) -> Result<Self, DurationNanosOutOfRangeError> {\n        Self::try_from_units(hours, SECONDS_IN_HOUR * NANOSECONDS_IN_SECOND, \"hours\")\n    }\n\n    /// Creates a duration from a number of whole days.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the result exceeds [`DurationNanos::MAX`].\n    #[must_use]","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/core/src/nanos.rs#L178-L214","documentation":"`DurationNanos::from_hours` converts whole hours (u64) to nanoseconds and panics when `hours * 3_600 * 1_000_000_000` exceeds `DurationNanos::MAX`. The panic arm replaces the Err of `try_from_hours` because this constructor is documented and designed to be usable in const contexts.","triggerScenarios":"Calling `DurationNanos::from_hours(h)` with `h > u64::MAX / 3_600_000_000_000` (i.e. `h >= 5_124_095_576` hours, about 584 years), so `try_from_hours` fails and the match panics.","commonSituations":"Config values like 'retention_hours' or 'session_hours' set to u64::MAX as a sentinel for 'never expire'; unit mix-ups (minutes/seconds passed as hours); extreme-value tests.","solutions":["Use `DurationNanos::try_from_hours(hours)` and handle Err.","Bound-check `hours <= u64::MAX / 3_600_000_000_000` before calling.","Clamp to `DurationNanos::MAX` for 'forever' semantics.","Fix the upstream unit so the value is in hours."],"exampleFix":"// before\nlet dur = DurationNanos::from_hours(retention_hours);\n\n// after\nlet dur = DurationNanos::try_from_hours(retention_hours)\n    .map_err(|_| ConfigError::OutOfRange(\"retention_hours\"))?;","handlingStrategy":"validation","validationCode":"const MAX_FROM_HOURS: u64 = u64::MAX / 3_600_000_000_000;\nfn can_convert_hours(h: u64) -> bool { h <= MAX_FROM_HOURS }","typeGuard":"fn fits_duration_hours(h: u64) -> Option<DurationNanos> {\n    DurationNanos::try_from_hours(h).ok()\n}","tryCatchPattern":null,"preventionTips":["Validate retention/session hour values against MAX_FROM_HOURS at config load.","Use try_from_hours for any externally sourced input.","Clamp deliberately (with a comment) if saturation is the intended semantics."],"tags":["rust","panic","integer-overflow","duration"],"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"}