{"record":{"id":"1796364efae231dc","repo":"nautechsystems/nautilus_trader","slug":"microseconds-must-be-finite-was-micros","errorCode":null,"errorMessage":"microseconds must be finite, was {micros}","messagePattern":"microseconds must be finite, was (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/core/src/datetime.rs","lineNumber":352,"sourceCode":"    millis_to_nanos(millis).expect(\"millis_to_nanos_unchecked: invalid or overflowing input\")\n}\n\n/// Converts microseconds (μs) to nanoseconds (ns).\n///\n/// Casting f64 to u64 by truncating the fractional part is intentional for unit conversion,\n/// which may lose precision and drop negative values after clamping.\n///\n/// # Errors\n///\n/// Returns an error if `micros` 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 micros_to_nanos(micros: f64) -> anyhow::Result<u64> {\n    anyhow::ensure!(\n        micros.is_finite(),\n        \"microseconds must be finite, was {micros}\"\n    );\n\n    if micros <= 0.0 {\n        return Ok(0);\n    }\n    let nanos = micros * NANOSECONDS_IN_MICROSECOND as f64;\n    anyhow::ensure!(\n        nanos < U64_UPPER_BOUND_F64,\n        \"microseconds {micros} is out of range for `u64` nanoseconds\"\n    );\n    Ok(nanos.trunc() as u64)\n}\n\n/// Converts microseconds (μs) to nanoseconds (ns), panicking on invalid input.\n///\n/// # Panics","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/core/src/datetime.rs#L334-L370","documentation":"Fired by micros_to_nanos when the f64 microsecond input cannot be converted to a u64 nanosecond count: it is non-finite (NaN or ±infinity) or its truncated, clamped value would overflow the u64 nanosecond range. It is a unit-conversion input validation guard protecting unchecked callers (micros_to_nanos_unchecked unwraps it).","triggerScenarios":"Calling micros_to_nanos(f64::NAN) or micros_to_nanos(±f64::INFINITY).","commonSituations":"NaN from malformed float parsing or 0/0 division upstream; infinity from overflowing arithmetic on timer deltas; missing-value sentinels (inf) in instrument definitions.","solutions":["Guard with micros.is_finite() before calling and handle the invalid value at the source.","Fix the upstream computation producing NaN/inf.","Use Option<f64>/Option<u64> for unknown or unset microsecond values instead of NaN/inf."],"exampleFix":"// before\nlet ns = micros_to_nanos(micros)?;\n// after\nif !micros.is_finite() { return Ok(0); }\nlet ns = micros_to_nanos(micros)?;","handlingStrategy":"validation","validationCode":"if !micros.is_finite() {\n    return Err(format!(\"microseconds not finite: {micros}\"));\n}","typeGuard":"fn is_convertible_micros(x: f64) -> bool { x.is_finite() && x > 0.0 && x < 1.8446744e16 }","tryCatchPattern":"let ns = match micros_to_nanos(micros) {\n    Ok(ns) => ns,\n    Err(e) if e.to_string().contains(\"must be finite\") => { log::warn!(\"non-finite micros input\"); 0 },\n    Err(e) => return Err(e),\n};","preventionTips":["Sanitize instrument-definition fields (e.g. ts_init, intervals) for NaN/inf before conversion.","Avoid inf as a 'missing' sentinel; use Option.","Validate parsed floats immediately after deserialization."],"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"}