pola-rs/polars · error

expected total ordering

Error message

expected total ordering

What it means

reorder_cmp uses PartialOrd::partial_cmp and unwraps with 'expected total ordering'; if partial_cmp returns None the type's comparison is not a total order (e.g. NaN floats) and it panics. Null handling is done after, so the panic fires for non-null values that cannot be ordered.

Solutions

  1. Sanitize data before sorting: replace NaN with null (fill_nan) or a sentinel so null handling takes over
  2. Use sort options that treat NaN explicitly, or drop/fillna NaN values first
  3. Verify custom element types implement a true total order (Ord, not just PartialOrd)

Example fix

// before
df.sort(["price"], SortMultipleOptions::default());
// after
let df = df.with_column(col("price").fill_nan(lit(f64::NAN).is_null())).unwrap(); // or fill_nan(lit(0.0))
df.sort(["price"], SortMultipleOptions::default());
Defensive patterns

Strategy: validation

Validate before calling

# Polars: remove non-orderable NaN before sorting
s = df["price"]
assert not s.drop_nulls().is_nan().any(), "NaN values break total ordering; use fill_nan or drop them"

Prevention

When it happens

Trigger: Sorting/ordering columns containing NaN or other values where partial_cmp yields None; calling arg_sort_multiple or any of its helpers (cmp_element_unchecked, cmp_at, etc.) on such data.

Common situations: Sorting float columns with NaN; user-defined or exotic types with inconsistent Ord; comparing mixed-type or corrupted data in multi-column sorts.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/59ba477912b3e076. Report an issue: GitHub.

Appendix: source

Thrown at crates/polars-utils/src/sort.rs:119

{
    fn cmp(&self, other: &Self) -> Ordering {
        reorder_cmp(&self.0, &other.0, DESCENDING, NULLS_LAST)
    }
}

/// Compare two values with support for sort direction and nulls position.
///
/// # Panics
///
/// Panics if `T::partial_cmp(lhs, rhs)` returns `None`.
#[inline]
pub fn reorder_cmp<T: PartialOrd + IsNull>(
    lhs: &T,
    rhs: &T,
    descending: bool,
    nulls_last: bool,
) -> Ordering {
    match PartialOrd::partial_cmp(lhs, rhs).expect("expected total ordering") {
        Ordering::Equal => Ordering::Equal,
        _ if lhs.is_null() && nulls_last => Ordering::Greater,
        _ if rhs.is_null() && nulls_last => Ordering::Less,
        _ if lhs.is_null() => Ordering::Less,
        _ if rhs.is_null() => Ordering::Greater,
        ord if descending => ord.reverse(),
        ord => ord,
    }
}

View on GitHub (pinned to fe841f959e)