pola-rs/polars · error

should be f64 scalar

Error message

should be f64 scalar

What it means

The DecimalChunked aggregation helper rescales result scalars by dividing by 10^scale, and assumes the scalar carries an f64 value (as produced by mean-style aggregations). If the scalar holds another dtype, try_extract::<f64>() fails and it panics 'should be f64 scalar'. This is an internal invariant between decimal aggregations and their scalar outputs.

Source

Thrown at crates/polars-core/src/series/implementations/decimal.rs:43

    fn apply_physical<T, F: Fn(&Int128Chunked) -> T>(&self, f: F) -> T {
        f(self.0.physical())
    }

    fn scale_factor(&self) -> u128 {
        10u128.pow(self.0.scale() as u32)
    }

    fn apply_scale(&self, mut scalar: Scalar) -> Scalar {
        if scalar.is_null() {
            return scalar;
        }

        debug_assert_eq!(scalar.dtype(), &DataType::Float64);
        let v = scalar
            .value()
            .try_extract::<f64>()
            .expect("should be f64 scalar");
        scalar.update((v / self.scale_factor() as f64).into());
        scalar
    }

    fn agg_helper<F: Fn(&Int128Chunked) -> Series>(&self, f: F, precision: usize) -> Series {
        let agg_s = f(self.0.physical());
        let scale = self.0.scale();
        match agg_s.dtype() {
            DataType::Int128 => {
                let ca = agg_s.i128().unwrap();
                let ca = ca.as_ref().clone();
                ca.into_decimal_unchecked(precision, scale).into_series()
            },
            DataType::List(dtype) if matches!(dtype.as_ref(), DataType::Int128) => {
                let dtype = self.0.dtype();
                let ca = agg_s.list().unwrap();
                let arr = ca.downcast_iter().next().unwrap();
                // SAFETY: dtype is passed correctly

View on GitHub (pinned to 68506541d2)

Solutions

  1. Align all polars crates on one exact version (cargo update / lockfile pin) so decimal aggregation helpers match
  2. Upgrade polars to the latest release; decimal aggregation invariants are actively fixed
  3. As a workaround, cast decimals to Float64 before the aggregation, then re-cast if needed

Example fix

# before
pl.col("price").mean()  # decimal mean path panics on mismatched versions

# after
pl.col("price").cast(pl.Float64).mean()  # bypass decimal helper until upgraded
Defensive patterns

Strategy: fallback

Validate before calling

fn is_decimal(s: &Series) -> bool {
    matches!(s.dtype(), DataType::Decimal(_, _))
}

Prevention

When it happens

Trigger: Aggregations over Decimal columns (mean, sum-then-normalize paths) where the aggregation returns a scalar whose dtype is not Float64 - typically from custom/user-defined aggregations plugged into the decimal path, or a mismatch between polars crate versions.

Common situations: Mixing polars-core and polars-lazy/plan versions so decimal aggregation helpers receive unexpected scalar types; custom plugin aggregations on decimal columns; encountered after upgrading one polars crate but not the others.

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@68506541d2 (2026-08-19). Data as JSON: /api/errors/9a39e5b839f26c92. Report an issue: GitHub.