nautechsystems/nautilus_trader · error

UnixNanos overflow in from_micros

Error message

UnixNanos overflow in from_micros

What it means

`UnixNanos::from_micros` converts a microsecond timestamp to nanoseconds using `checked_mul(NANOSECONDS_IN_MICROSECOND)` and panics on u64 overflow. Overflow means the microsecond value exceeds about 18,446,744,073,709,551 microseconds since epoch, outside the representable UnixNanos range.

Source

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

    /// Creates a new [`UnixNanos`] from a signed millisecond timestamp.
    ///
    /// Returns `None` if `millis` is negative or the result overflows `u64`.
    #[must_use]
    pub const fn from_millis_checked(millis: i64) -> Option<Self> {
        Self::from_units_checked(millis, NANOSECONDS_IN_MILLISECOND)
    }

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

    /// Creates a new [`UnixNanos`] from a signed microsecond timestamp.
    ///
    /// Returns `None` if `micros` is negative or the result overflows `u64`.
    #[must_use]
    pub const fn from_micros_checked(micros: i64) -> Option<Self> {
        Self::from_units_checked(micros, NANOSECONDS_IN_MICROSECOND)
    }

    const fn from_units_checked(value: i64, nanos_per_unit: u64) -> Option<Self> {
        if value < 0 {
            return None;
        }

        match value.cast_unsigned().checked_mul(nanos_per_unit) {
            Some(nanos) => Some(Self(nanos)),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the input precision and pick the matching constructor (from_millis, from_seconds, or direct `UnixNanos::new` for nanos).
  2. Validate `micros <= u64::MAX / 1_000` before the call.
  3. Use a checked path (`micros.checked_mul(1_000)`) and handle None gracefully.
  4. Clamp or reject out-of-range timestamps at the data-ingestion boundary.

Example fix

// before
let ts = UnixNanos::from_micros(event_ts_us);

// after
let ts = match event_ts_us.checked_mul(1_000) {
    Some(n) => UnixNanos::new(n),
    None => return Err(anyhow!("event_ts_us out of UnixNanos range")),
};
Defensive patterns

Strategy: validation

Validate before calling

const MAX_UNIXNANOS_MICROS: u64 = u64::MAX / 1_000;
fn valid_epoch_micros(u: u64) -> bool { u <= MAX_UNIXNANOS_MICROS }

Type guard

fn unix_nanos_from_micros(u: u64) -> Option<UnixNanos> {
    u.checked_mul(1_000).map(UnixNanos::new)
}

Prevention

When it happens

Trigger: Calling `UnixNanos::from_micros(u)` with `u > 18_446_744_073_709` (u64::MAX / 1e3), so checked_mul returns None and the panic fires.

Common situations: Unit confusion: passing nanosecond or millisecond values into from_micros; timestamps from external systems (e.g. telemetry) in different precision; sentinel max-u64 values meaning 'infinite expiry'.

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/795ae82b97307c1e. Report an issue: GitHub.