nautechsystems/nautilus_trader · error

seconds must be finite, was {secs}

Error message

seconds must be finite, was {secs}

What it means

secs_to_nanos converts seconds (f64) to u64 nanoseconds. NaN and infinities cannot be represented as an integer nanosecond count, so the function rejects any non-finite input with this error instead of returning a garbage value.

Source

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

    Weekday::Tuesday,
    Weekday::Wednesday,
    Weekday::Thursday,
    Weekday::Friday,
];

/// Converts seconds to nanoseconds (ns).
///
/// # Errors
///
/// Returns an error if `secs` is non-finite or cannot be represented as `u64` nanoseconds.
#[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_nanos(secs: f64) -> anyhow::Result<u64> {
    anyhow::ensure!(secs.is_finite(), "seconds must be finite, was {secs}");
    if secs <= 0.0 {
        return Ok(0);
    }
    let nanos = secs * NANOSECONDS_IN_SECOND as f64;
    anyhow::ensure!(
        nanos < U64_UPPER_BOUND_F64,
        "seconds {secs} is out of range for `u64` nanoseconds"
    );
    Ok(nanos.trunc() as u64)
}

/// 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,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check secs.is_finite() before calling and handle the invalid value at the source.
  2. Trace where the f64 originated; fix the computation producing NaN/infinity (often division by zero).
  3. If infinity is intentional as 'no expiry', use an option/sentinel pattern instead of a float.

Example fix

// before
let ns = secs_to_nanos(elapsed_secs)?;
// after
if !elapsed_secs.is_finite() {
    anyhow::bail!("elapsed_secs not finite: {elapsed_secs}");
}
let ns = secs_to_nanos(elapsed_secs)?;
Defensive patterns

Strategy: validation

Validate before calling

if !secs.is_finite() {
    return Err(format!("seconds not finite: {secs}"));
}

Type guard

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

Try / catch

let ns = match secs_to_nanos(secs) {
    Ok(ns) => ns,
    Err(e) if e.to_string().contains("must be finite") => { log::warn!("non-finite seconds input"); 0 },
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling secs_to_nanos(f64::NAN), secs_to_nanos(f64::INFINITY) or secs_to_nanos(f64::NEG_INFINITY).

Common situations: Division by zero upstream producing NaN (e.g. elapsed/duration where duration is 0); uninitialized or default-initialized float fields; parsing timestamps from data where missing values were encoded as inf.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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