databendlabs/databend · error
FloatIsNan
Error message
FloatIsNan
What it means
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.
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.
Example fix
// before
let f = OrderedFloat::try_from(a / b)?; // panics/errors when b==0 => NaN
// after
let v = if b == 0.0 { 0.0 } else { a / b };
let f = OrderedFloat::try_from(v)?; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_not_nan(v: f64) -> Result<OrderedFloat<f64>, String> {
if v.is_nan() { Err("NaN not allowed".into()) } else { Ok(OrderedFloat(v)) }
} Type guard
fn is_finite_f64(v: f64) -> bool { v.is_finite() } Try / catch
match OrderedFloat::try_from(value) {
Err(_) => sanitize_or_null(value), // replace NaN per schema rules
Ok(v) => write(v),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- Failed to convert RaftStoreEntry to SMEntry
- expected a non-NaN float
- hybrid bitmap small set size overflow
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/6491bfa0c97749fd.
Report an issue: GitHub.
Appendix: source
Thrown at src/common/base/src/base/ordered_float.rs:1631
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct FloatIsNan;
impl Error for FloatIsNan {
fn description(&self) -> &str {
"NotNan constructed with NaN"
}
}
impl fmt::Display for FloatIsNan {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NotNan constructed with NaN")
}
}
impl From<FloatIsNan> for std::io::Error {
#[inline]
fn from(e: FloatIsNan) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::InvalidInput, e)
}
}
#[inline]
/// Used for hashing. Input must not be zero or NaN.
fn raw_double_bits<F: FloatCore>(f: &F) -> u64 {
let (man, exp, sign) = f.integer_decode();
let exp_u64 = exp as u16 as u64;
let sign_u64 = (sign > 0) as u64;
(man & MAN_MASK) | ((exp_u64 << 52) & EXP_MASK) | ((sign_u64 << 63) & SIGN_MASK)
}
impl<T: FloatCore> Zero for NotNan<T> {
#[inline]
fn zero() -> Self {
NotNan(T::zero())
}
View on GitHub (pinned to 288d84d76e)