{"record":{"id":"7c2c736187c893b6","repo":"databendlabs/databend","slug":"expected-a-non-nan-float","errorCode":null,"errorMessage":"expected a non-NaN float","messagePattern":"expected a non-NaN float","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/base/src/base/ordered_float.rs","lineNumber":2075,"sourceCode":"    }\n\n    impl<T> borsh::BorshSerialize for NotNan<T>\n    where T: borsh::BorshSerialize\n    {\n        #[inline]\n        fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {\n            <T as borsh::BorshSerialize>::serialize(&self.0, writer)\n        }\n    }\n\n    impl<T> borsh::BorshDeserialize for NotNan<T>\n    where T: FloatCore + borsh::BorshDeserialize\n    {\n        #[inline]\n        fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {\n            let float = <T as borsh::BorshDeserialize>::deserialize_reader(reader)?;\n            NotNan::new(float).map_err(|_| {\n                borsh::io::Error::new(\n                    borsh::io::ErrorKind::InvalidData,\n                    \"expected a non-NaN float\",\n                )\n            })\n        }\n    }\n\n    #[test]\n    fn test_ordered_float() {\n        let float = OrderedFloat(1.0f64);\n        let buffer = borsh::to_vec(&float).expect(\"failed to serialize value\");\n        let deser_float: OrderedFloat<f64> =\n            borsh::from_slice(&buffer).expect(\"failed to deserialize value\");\n        assert_eq!(deser_float, float);\n    }\n\n    #[test]\n    fn test_not_nan() {","sourceCodeStart":2057,"sourceCodeEnd":2093,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/common/base/src/base/ordered_float.rs#L2057-L2093","documentation":"The NotNan wrapper (ordered_float) rejects float values that are NaN during borsh deserialization. After reading a raw float from the reader, NotNan::new is applied; NaN is not orderable, so the library refuses to construct NotNan<T> from it and maps the failure to a borsh io error with kind InvalidData. This guarantees every deserialized NotNan value is a valid, comparable float.","triggerScenarios":"Borsh-deserializing a NotNan<T> (e.g. NotNan<f64>) whose underlying bytes encode NaN — i.e. the serialized payload contains an all-exponent-and-mantissa-bits-set float (exponent all 1s, mantissa nonzero).","commonSituations":"Corrupted or hand-crafted serialized payloads; writing raw f64::NAN or computed NaN (0.0/0.0) into a slot later read back as NotNan; cross-version data where the producer used plain f64 while the consumer expects NotNan.","solutions":["Fix the producer to never serialize NaN into NotNan fields (check for is_nan() before writing).","Validate/sanitize the source data that produced the NaN before serialization.","If NaN is legitimately possible, change the schema to Option<NotNan<T>> or serialize plain T and wrap with NotNan::new on read, handling the error explicitly."],"exampleFix":"// before\nlet v: NotNan<f64> = NotNan::deserialize_reader(reader)?;\n\n// after\nlet raw = f64::deserialize_reader(reader)?;\nlet v = NotNan::new(raw).map_err(|_| borsh::io::Error::new(\n    borsh::io::ErrorKind::InvalidData,\n    \"expected a non-NaN float\",\n))?;","handlingStrategy":"validation","validationCode":"if raw.is_nan() {\n    return Err(anyhow!(\"value is NaN; cannot be stored as NotNan\"));\n}","typeGuard":"fn is_valid_notnan(v: f64) -> bool { !v.is_nan() }","tryCatchPattern":null,"preventionTips":["Never write NaN into fields deserialized as NotNan.","Check is_nan() at computation boundaries (divisions, sqrt of negatives).","Use Option<NotNan<T>> when absence/NaN is a legitimate state."],"tags":["serialization","rust","deserialization","float"],"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"}