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
- Check secs.is_finite() before calling and handle the invalid value at the source.
- Trace where the f64 originated; fix the computation producing NaN/infinity (often division by zero).
- 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
- Check is_finite() on any f64 derived from division before converting units.
- Avoid NaN/inf as sentinel values; prefer Option types.
- Fuzz or property-test unit conversion helpers with non-finite inputs.
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
- milliseconds must be finite, was {millis}
- microseconds must be finite, was {micros}
- {e}
- {FAILED}: {e}
- {FAILED}: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2c392af30aa947cb.
Report an issue: GitHub.