{"record":{"id":"2c392af30aa947cb","repo":"nautechsystems/nautilus_trader","slug":"seconds-must-be-finite-was-secs","errorCode":null,"errorMessage":"seconds must be finite, was {secs}","messagePattern":"seconds must be finite, was (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/core/src/datetime.rs","lineNumber":231,"sourceCode":"    Weekday::Tuesday,\n    Weekday::Wednesday,\n    Weekday::Thursday,\n    Weekday::Friday,\n];\n\n/// Converts seconds to nanoseconds (ns).\n///\n/// # Errors\n///\n/// Returns an error if `secs` is non-finite or cannot be represented as `u64` nanoseconds.\n#[expect(\n    clippy::cast_possible_truncation,\n    clippy::cast_sign_loss,\n    clippy::cast_precision_loss,\n    reason = \"Intentional for unit conversion, may lose precision after clamping\"\n)]\npub fn secs_to_nanos(secs: f64) -> anyhow::Result<u64> {\n    anyhow::ensure!(secs.is_finite(), \"seconds must be finite, was {secs}\");\n    if secs <= 0.0 {\n        return Ok(0);\n    }\n    let nanos = secs * NANOSECONDS_IN_SECOND as f64;\n    anyhow::ensure!(\n        nanos < U64_UPPER_BOUND_F64,\n        \"seconds {secs} is out of range for `u64` nanoseconds\"\n    );\n    Ok(nanos.trunc() as u64)\n}\n\n/// Converts seconds to milliseconds (ms).\n///\n/// # Errors\n///\n/// Returns an error if `secs` is non-finite or cannot be represented as `u64` milliseconds.\n#[expect(\n    clippy::cast_possible_truncation,","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/core/src/datetime.rs#L213-L249","documentation":"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.","triggerScenarios":"Calling secs_to_nanos(f64::NAN), secs_to_nanos(f64::INFINITY) or secs_to_nanos(f64::NEG_INFINITY).","commonSituations":"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.","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."],"exampleFix":"// before\nlet ns = secs_to_nanos(elapsed_secs)?;\n// after\nif !elapsed_secs.is_finite() {\n    anyhow::bail!(\"elapsed_secs not finite: {elapsed_secs}\");\n}\nlet ns = secs_to_nanos(elapsed_secs)?;","handlingStrategy":"validation","validationCode":"if !secs.is_finite() {\n    return Err(format!(\"seconds not finite: {secs}\"));\n}","typeGuard":"fn is_convertible_secs(x: f64) -> bool { x.is_finite() && x > 0.0 && x < 1.8446744e10 }","tryCatchPattern":"let ns = match secs_to_nanos(secs) {\n    Ok(ns) => ns,\n    Err(e) if e.to_string().contains(\"must be finite\") => { log::warn!(\"non-finite seconds input\"); 0 },\n    Err(e) => return Err(e),\n};","preventionTips":["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."],"tags":["rust","datetime","floating-point","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}