nautechsystems/nautilus_trader · error

seconds {secs} is out of range for `u64` milliseconds

Error message

seconds {secs} is out of range for `u64` milliseconds

What it means

secs_to_millis stores the result in u64, which caps at ~1.8446744e19 milliseconds (about 5.8e8 years). This error means the input seconds exceed that range after conversion, so the conversion would overflow.

Source

Thrown at crates/core/src/datetime.rs:260

/// Converts seconds to milliseconds (ms).
///
/// # Errors
///
/// Returns an error if `secs` is non-finite or cannot be represented as `u64` milliseconds.
#[expect(
    clippy::cast_possible_truncation,
    clippy::cast_sign_loss,
    clippy::cast_precision_loss,
    reason = "Intentional for unit conversion, may lose precision after clamping"
)]
pub fn secs_to_millis(secs: f64) -> anyhow::Result<u64> {
    anyhow::ensure!(secs.is_finite(), "seconds must be finite, was {secs}");
    if secs <= 0.0 {
        return Ok(0);
    }
    let millis = secs * MILLISECONDS_IN_SECOND as f64;
    anyhow::ensure!(
        millis < U64_UPPER_BOUND_F64,
        "seconds {secs} is out of range for `u64` milliseconds"
    );
    Ok(millis.trunc() as u64)
}

/// Converts seconds to nanoseconds (ns), panicking on invalid input.
///
/// This is a convenience wrapper around [`secs_to_nanos`] when the caller expects
/// the input to be trusted and in-range.
///
/// # Panics
///
/// Panics if [`secs_to_nanos`] would return an error for `secs`.
#[must_use]
pub fn secs_to_nanos_unchecked(secs: f64) -> u64 {
    secs_to_nanos(secs).expect("secs_to_nanos_unchecked: invalid or overflowing input")
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the input unit; convert (divide) if the value is in ms/us/ns not seconds.
  2. Range-check the seconds value before conversion and reject/clamp implausible values.
  3. Validate the source data (config/JSON) with an explicit upper bound.

Example fix

// before
let ms = secs_to_millis(nano_value as f64)?; // overflow
// after
let ms = secs_to_millis(nano_value as f64 / 1_000_000_000.0)?;
Defensive patterns

Strategy: validation

Validate before calling

const MAX_SECS_F64: f64 = 1.8446744e16; // u64::MAX milliseconds in seconds
if !secs.is_finite() || secs < 0.0 || secs >= MAX_SECS_F64 {
    return Err(format!("seconds out of convertible range: {secs}"));
}

Type guard

fn is_convertible_secs(x: f64) -> bool { x.is_finite() && x > 0.0 && x < 1.8446744e16 }

Try / catch

let ms = match secs_to_millis(secs) {
    Ok(ms) => ms,
    Err(e) if e.to_string().contains("out of range") => u64::MAX, // or bail with a unit check
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling secs_to_millis with secs where secs * 1e3 >= U64_UPPER_BOUND_F64 (secs >= ~1.8446744e16 seconds).

Common situations: Unit confusion — passing nanosecond or microsecond counts as seconds; parsing an unvalidated numeric field from config or a wire payload that contains an absurd value.

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/6120e1056505ff37. Report an issue: GitHub.