nautechsystems/nautilus_trader · error

UnixNanos overflow in from_seconds

Error message

UnixNanos overflow in from_seconds

What it means

`UnixNanos::from_seconds` converts a Unix timestamp in whole seconds to nanoseconds by multiplying with `NANOSECONDS_IN_SECOND` (1e9) using `checked_mul`. If the product overflows u64, the constructor panics, since a UnixNanos timestamp cannot exceed `u64::MAX` nanoseconds (year 2554). The library throws it to fail fast rather than silently wrapping the timestamp.

Source

Thrown at crates/core/src/nanos.rs:430

        self.0 / NANOSECONDS_IN_MILLISECOND
    }

    /// Returns the timestamp as microseconds, truncating sub-microsecond precision.
    #[must_use]
    pub const fn as_micros(&self) -> u64 {
        self.0 / NANOSECONDS_IN_MICROSECOND
    }

    /// Creates a new [`UnixNanos`] from a second timestamp.
    ///
    /// # Panics
    ///
    /// Panics if the result overflows `u64`.
    #[must_use]
    pub const fn from_seconds(seconds: u64) -> Self {
        match seconds.checked_mul(NANOSECONDS_IN_SECOND) {
            Some(nanos) => Self(nanos),
            None => panic!("UnixNanos overflow in from_seconds"),
        }
    }

    /// Creates a new [`UnixNanos`] from a millisecond timestamp.
    ///
    /// # Panics
    ///
    /// Panics if the result overflows `u64`.
    #[must_use]
    pub const fn from_millis(millis: u64) -> Self {
        match millis.checked_mul(NANOSECONDS_IN_MILLISECOND) {
            Some(nanos) => Self(nanos),
            None => panic!("UnixNanos overflow in from_millis"),
        }
    }

    /// Creates a new [`UnixNanos`] from a signed millisecond timestamp.
    ///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the value is truly in seconds; if it is in milliseconds use `from_millis`, if nanoseconds use `UnixNanos::new`/`from_raw`.
  2. Guard the input: reject values greater than `u64::MAX / 1_000_000_000` before the call.
  3. Use a checked/clamped path: compute `seconds.checked_mul(1_000_000_000)` yourself and handle None.
  4. Sanity-check timestamps against a plausible range (e.g. 0..=year 2200) at config load time.

Example fix

// before
let ts = UnixNanos::from_seconds(expiry); // may be millis!

// after
let ts = if expiry > 18_446_744_073 {
    UnixNanos::from_millis(expiry) // value was actually milliseconds
} else {
    UnixNanos::from_seconds(expiry)
};
Defensive patterns

Strategy: validation

Validate before calling

const MAX_UNIXNANOS_SECS: u64 = u64::MAX / 1_000_000_000;
fn valid_epoch_seconds(s: u64) -> bool { s <= MAX_UNIXNANOS_SECS }

Type guard

fn unix_nanos_from_seconds(s: u64) -> Option<UnixNanos> {
    s.checked_mul(1_000_000_000).map(UnixNanos::new)
}

Prevention

When it happens

Trigger: Calling `UnixNanos::from_seconds(s)` with `s > 18_446_744_073` (u64::MAX / 1e9, i.e. timestamps beyond roughly year 2554), so `checked_mul` returns None and the panic arm executes.

Common situations: Parsing epoch seconds from a config/env var where someone entered nanoseconds or milliseconds by mistake; sentinel u64::MAX used for 'no expiry'; deserializing timestamps from external data with wrong units.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/b6024abba6e41389. Report an issue: GitHub.