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

  1. Filter or sanitize NaN values before writing: replace with NULL, 0.0, or a sentinel per your schema rules.
  2. 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.
  3. Trace where NaN originated in the computation (division by zero, sqrt of negative, log of non-positive) and fix the upstream math.
  4. 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

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


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)