nautechsystems/nautilus_trader · error

second timestamp {secs} overflows when scaled to nanoseconds

Error message

second timestamp {secs} overflows when scaled to nanoseconds

What it means

parse_secs_to_nanos converts a Unix-seconds timestamp to nanoseconds using secs.checked_mul(NANOSECONDS_IN_SECOND). If secs * 1_000_000_000 would overflow u64 (secs > ~18.4 billion, i.e. year ~2554), the error is raised to avoid silent wraparound in release builds.

Source

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

///
/// # 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`].
///
/// Negative inputs are mapped to `Ok(None)` so callers can model fields
/// where the wire uses `-1` (or any negative sentinel) as "absent". Fields
/// that overload `0` with a separate meaning (e.g. `0` for IOC on
/// `OrderInfo::order_expiry`) must apply that interpretation at the call
/// site; this parser treats `0` as a literal Unix epoch timestamp.
///
/// # Errors
///
/// Returns an error if a non-negative `millis` overflows when scaled to
/// nanoseconds.
pub fn parse_optional_millis_to_nanos(millis: i64) -> anyhow::Result<Option<UnixNanos>> {
    if millis < 0 {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the field is in seconds; if it is milliseconds use parse_millis_to_nanos instead.
  2. Sanity-check the timestamp range (e.g. reject values outside a plausible window) before conversion.
  3. Inspect the raw payload; treat overflowing values as malformed messages and drop them.

Example fix

// before
let ts = parse_secs_to_nanos(millis_field)?; // field is milliseconds
// after
let ts = parse_millis_to_nanos(millis_field)?;
Defensive patterns

Strategy: validation

Validate before calling

fn secs_plausible(secs: u64) -> bool { (1_400_000_000..u64::MAX / 1_000_000_000).contains(&secs) }

Type guard

fn is_secs(v: u64) -> bool { (1_400_000_000..18_446_744_073).contains(&v) }

Try / catch

let ts = parse_secs_to_nanos(secs)
    .map_err(|e| { warn!("funding ts overflow: {e}"); e })?;

Prevention

When it happens

Trigger: Calling parse_secs_to_nanos (directly, from parse_funding_rate_update, or from the parse_secs_to_nanos_rejects_overflow test) with a seconds value above ~1.844e10 — e.g. a corrupted funding-rate timestamp field or seconds passed as milliseconds.

Common situations: Passing a millisecond value where seconds are expected (1000x too large for normal dates is fine, but far-future/corrupt values overflow); malformed funding rate updates from the venue.

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/800998755ab54c27. Report an issue: GitHub.