databendlabs/databend · error
expected a non-NaN float
Error message
expected a non-NaN float
What it means
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.
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.
Example fix
// before
let v: NotNan<f64> = NotNan::deserialize_reader(reader)?;
// after
let raw = f64::deserialize_reader(reader)?;
let v = NotNan::new(raw).map_err(|_| borsh::io::Error::new(
borsh::io::ErrorKind::InvalidData,
"expected a non-NaN float",
))?; Defensive patterns
Strategy: validation
Validate before calling
if raw.is_nan() {
return Err(anyhow!("value is NaN; cannot be stored as NotNan"));
} Type guard
fn is_valid_notnan(v: f64) -> bool { !v.is_nan() } Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- internal error: entered unreachable code
- Unimplemented serialize DataSourceMeta
- Unimplemented deserialize DataSourceMeta
- hybrid bitmap small set size overflow
- data is truncated or invalid
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/7c2c736187c893b6.
Report an issue: GitHub.
Appendix: source
Thrown at src/common/base/src/base/ordered_float.rs:2075
}
impl<T> borsh::BorshSerialize for NotNan<T>
where T: borsh::BorshSerialize
{
#[inline]
fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
<T as borsh::BorshSerialize>::serialize(&self.0, writer)
}
}
impl<T> borsh::BorshDeserialize for NotNan<T>
where T: FloatCore + borsh::BorshDeserialize
{
#[inline]
fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
let float = <T as borsh::BorshDeserialize>::deserialize_reader(reader)?;
NotNan::new(float).map_err(|_| {
borsh::io::Error::new(
borsh::io::ErrorKind::InvalidData,
"expected a non-NaN float",
)
})
}
}
#[test]
fn test_ordered_float() {
let float = OrderedFloat(1.0f64);
let buffer = borsh::to_vec(&float).expect("failed to serialize value");
let deser_float: OrderedFloat<f64> =
borsh::from_slice(&buffer).expect("failed to deserialize value");
assert_eq!(deser_float, float);
}
#[test]
fn test_not_nan() {View on GitHub (pinned to 288d84d76e)