{"record":{"id":"254bb542844e65cb","repo":"databendlabs/databend","slug":"addition-resulted-in-nan","errorCode":null,"errorMessage":"Addition resulted in NaN","messagePattern":"Addition resulted in NaN","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/base/src/base/ordered_float.rs","lineNumber":1320,"sourceCode":"\nimpl<T: FloatCore + PartialEq> Eq for NotNan<T> {}\n\nimpl<T: FloatCore> PartialEq<T> for NotNan<T> {\n    #[inline]\n    fn eq(&self, other: &T) -> bool {\n        self.0 == *other\n    }\n}\n\n/// Adds a float directly.\n///\n/// Panics if the provided value is NaN or the computation results in NaN\nimpl<T: FloatCore> Add<T> for NotNan<T> {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, other: T) -> Self {\n        NotNan::new(self.0 + other).expect(\"Addition resulted in NaN\")\n    }\n}\n\n/// Adds a float directly.\n///\n/// Panics if the provided value is NaN.\nimpl<T: FloatCore + Sum> Sum for NotNan<T> {\n    fn sum<I: Iterator<Item = NotNan<T>>>(iter: I) -> Self {\n        NotNan::new(iter.map(|v| v.0).sum()).expect(\"Sum resulted in NaN\")\n    }\n}\n\nimpl<'a, T: FloatCore + Sum + 'a> Sum<&'a NotNan<T>> for NotNan<T> {\n    #[inline]\n    fn sum<I: Iterator<Item = &'a NotNan<T>>>(iter: I) -> Self {\n        iter.cloned().sum()\n    }\n}","sourceCodeStart":1302,"sourceCodeEnd":1338,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/common/base/src/base/ordered_float.rs#L1302-L1338","documentation":"The NotNan<T> wrapper in ordered_float.rs forbids NaN values so that Ord can be implemented soundly. The Add impl adds the wrapped float to a raw T and calls NotNan::new, which panics with this message when the result is NaN (e.g. inf + -inf). It is a fail-fast invariant check, not a recoverable library error.","triggerScenarios":"Calling `+` (std::ops::Add<T> for NotNan<T>) where self.0 + other yields NaN: inf + (-inf), inf - inf via negative operand, 0.0 + NaN operand, or adding a raw NaN value to a NotNan number.","commonSituations":"Accumulating float results from numeric pipelines where infinities were produced earlier (overflowing division by zero), or adding an unchecked/unvalidated user-supplied float that is NaN to a NotNan value in aggregation or scoring code.","solutions":["Check operands for NaN/infinity before adding: skip or clamp non-finite values with `is_finite()`.","Replace the `+` operator with `NotNan::new(a.0 + b).ok()` and handle the None case explicitly.","Sanitize raw floats at the boundary with `NotNan::new(x).map_err(..)` before they ever reach NotNan arithmetic."],"exampleFix":"// before\nlet total = notnan_total + raw_delta; // panics if result is NaN\n// after\nlet total = if delta.is_finite() { notnan_total + delta } else { notnan_total };","handlingStrategy":"validation","validationCode":"fn can_add(a: &NotNan<f64>, b: f64) -> bool {\n    a.is_finite() && b.is_finite() && !(a.0.is_infinite() && b == f64::NEG_INFINITY || a.0 == f64::INFINITY && b.is_infinite() && (a.0 + b).is_nan())\n}","typeGuard":"fn is_finite_f64(x: f64) -> bool { x.is_finite() }","tryCatchPattern":"// Rust panics are not catchable with try/catch; use catch_unwind only at task boundaries:\nlet r = std::panic::catch_unwind(|| notnan_a + raw_b).ok();","preventionTips":["Never add raw unvalidated floats to NotNan values; construct via NotNan::new first.","Check is_finite() on every operand entering arithmetic pipelines.","Guard division-by-zero upstream so operands never become inf.","Prefer fallible NotNan::new(...).ok() over operator arithmetic in risky paths."],"tags":["rust","float","nan","arithmetic","panic"],"backgroundTag":"nan-arithmetic-panic","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"}