{"record":{"id":"6491bfa0c97749fd","repo":"databendlabs/databend","slug":"floatisnan","errorCode":null,"errorMessage":"FloatIsNan","messagePattern":"FloatIsNan","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/base/src/base/ordered_float.rs","lineNumber":1631,"sourceCode":"#[derive(Copy, Clone, PartialEq, Eq, Debug)]\npub struct FloatIsNan;\n\nimpl Error for FloatIsNan {\n    fn description(&self) -> &str {\n        \"NotNan constructed with NaN\"\n    }\n}\n\nimpl fmt::Display for FloatIsNan {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"NotNan constructed with NaN\")\n    }\n}\n\nimpl From<FloatIsNan> for std::io::Error {\n    #[inline]\n    fn from(e: FloatIsNan) -> std::io::Error {\n        std::io::Error::new(std::io::ErrorKind::InvalidInput, e)\n    }\n}\n\n#[inline]\n/// Used for hashing. Input must not be zero or NaN.\nfn raw_double_bits<F: FloatCore>(f: &F) -> u64 {\n    let (man, exp, sign) = f.integer_decode();\n    let exp_u64 = exp as u16 as u64;\n    let sign_u64 = (sign > 0) as u64;\n    (man & MAN_MASK) | ((exp_u64 << 52) & EXP_MASK) | ((sign_u64 << 63) & SIGN_MASK)\n}\n\nimpl<T: FloatCore> Zero for NotNan<T> {\n    #[inline]\n    fn zero() -> Self {\n        NotNan(T::zero())\n    }\n","sourceCodeStart":1613,"sourceCodeEnd":1649,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/common/base/src/base/ordered_float.rs#L1613-L1649","documentation":"This is the `From<FloatIsNan> for std::io::Error` conversion: when ordered-float operations encounter a NaN where it is forbidden (ordered floats guarantee a total order and disallow NaN), the `FloatIsNan` error is converted into an `io::Error` of kind `InvalidInput` whose message is simply `FloatIsNan`. It surfaces when serialization/hash paths use ordered floats with invalid input.","triggerScenarios":"Constructing an `OrderedFloat`/`NotNan` from `f32::NAN` or `f64::NAN` (e.g. via `try_from`/`new` returning Err) inside code that converts the error into io::Error — notably serialization paths that use `raw_double_bits`-backed hashing or writers that bubble the error as io::Error.","commonSituations":"NaN values reaching a writer from computed divisions like 0.0/0.0, parsing floating-point data files that contain NaN where the schema forbids it, aggregate results producing NaN that are then written/serialized.","solutions":["Filter or sanitize NaN values before writing: replace with NULL, 0.0, or a sentinel per your schema rules.","Use `NotNan::try_from`/`new` at the data-ingestion boundary and handle the Err there instead of letting it become an io::Error mid-write.","Trace where NaN originated in the computation (division by zero, sqrt of negative, log of non-positive) and fix the upstream math.","If NaN is legitimate in your data, avoid ordered-float-based encoders for those columns."],"exampleFix":"// before\nlet f = OrderedFloat::try_from(a / b)?; // panics/errors when b==0 => NaN\n// after\nlet v = if b == 0.0 { 0.0 } else { a / b };\nlet f = OrderedFloat::try_from(v)?;","handlingStrategy":"validation","validationCode":"fn ensure_not_nan(v: f64) -> Result<OrderedFloat<f64>, String> {\n    if v.is_nan() { Err(\"NaN not allowed\".into()) } else { Ok(OrderedFloat(v)) }\n}","typeGuard":"fn is_finite_f64(v: f64) -> bool { v.is_finite() }","tryCatchPattern":"match OrderedFloat::try_from(value) {\n    Err(_) => sanitize_or_null(value), // replace NaN per schema rules\n    Ok(v) => write(v),\n}","preventionTips":["Sanitize NaN at ingestion boundaries (map to NULL or sentinel)","Fix upstream math that produces NaN (0/0, sqrt(-x))","Reject NaN columns in schema validation before serialization"],"tags":["floats","nan","serialization"],"backgroundTag":"invalid-argument-value","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}