nautechsystems/nautilus_trader · error

microsecond timestamp {micros} overflows when scaled to nano

Error message

microsecond timestamp {micros} overflows when scaled to nanoseconds

What it means

parse_micros_to_nanos converts a microsecond timestamp to UnixNanos via micros.checked_mul(1_000). When that multiplication would overflow u64 the error is raised instead of allowing silent wraparound. Only values above u64::MAX / 1_000 (~1.8e16 microseconds) trigger it, far beyond any real timestamp.

Source

Thrown at crates/adapters/lighter/src/common/parse.rs:185

/// venue timestamps are nowhere near this bound; the check rejects malformed
/// payloads instead of silently wrapping in release builds.
pub fn parse_millis_to_nanos(millis: u64) -> anyhow::Result<UnixNanos> {
    let nanos = millis
        .checked_mul(NANOSECONDS_IN_MILLISECOND)
        .ok_or_else(|| {
            anyhow::anyhow!("millisecond timestamp {millis} overflows when scaled to nanoseconds")
        })?;
    Ok(UnixNanos::from(nanos))
}

/// Converts a Unix microsecond timestamp into [`UnixNanos`].
///
/// # Errors
///
/// Returns an error if `micros * 1_000` would overflow `u64`.
pub fn parse_micros_to_nanos(micros: u64) -> anyhow::Result<UnixNanos> {
    let nanos = micros.checked_mul(1_000).ok_or_else(|| {
        anyhow::anyhow!("microsecond timestamp {micros} overflows when scaled to nanoseconds")
    })?;
    Ok(UnixNanos::from(nanos))
}

/// Converts a Unix second timestamp into [`UnixNanos`].
///
/// # Errors
///
/// Returns an error if `secs * 1_000_000_000` would overflow `u64`.
pub fn parse_secs_to_nanos(secs: u64) -> anyhow::Result<UnixNanos> {
    let nanos = secs.checked_mul(NANOSECONDS_IN_SECOND).ok_or_else(|| {
        anyhow::anyhow!("second timestamp {secs} overflows when scaled to nanoseconds")
    })?;
    Ok(UnixNanos::from(nanos))
}

/// Converts a signed Unix millisecond timestamp into [`UnixNanos`].
///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the timestamp unit; if the source value is already nanoseconds, use it directly without scaling.
  2. Validate/bound the incoming timestamp before conversion.
  3. For testing, keep values below u64::MAX / 1_000.

Example fix

// before
let ts = parse_micros_to_nanos(nanos_value)?; // value is already nanos
// after
let ts = UnixNanos::from(nanos_value);
Defensive patterns

Strategy: validation

Validate before calling

fn micros_plausible(us: u64) -> bool { us < u64::MAX / 1_000 && us > 1_400_000_000_000_000 }

Type guard

fn is_micros(v: u64) -> bool { (1_400_000_000_000_000..u64::MAX / 1_000).contains(&v) }

Try / catch

match parse_micros_to_nanos(micros) {
    Ok(ts) => ts,
    Err(e) => { warn!("micros overflow: {e}"); UnixNanos::default() }
}

Prevention

When it happens

Trigger: Calling parse_micros_to_nanos directly (its only caller is the test parse_micros_to_nanos_rejects_overflow) with micros > ~1.8e16, i.e. a corrupt or deliberately oversized value.

Common situations: Feeding nanosecond values into the microsecond converter; corrupted venue payloads; overflow unit tests.

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/2642f06e2420a23e. Report an issue: GitHub.