pola-rs/polars · error

Wrong DataType

Error message

Wrong DataType

What it means

Compile-time panic from #[polars_expr] in pyo3-polars-derive (lib.rs:130). The macro supports at most three parameters: the mandatory input slice plus at most context and kwargs. Any function with three or more parameters after the first panics with "didn't expect so many arguments" — the extra arguments are not even inspected, the count alone is rejected. Additional configuration has no positional channel; it must ride inside the kwargs struct.

Source

Thrown at crates/polars-arrow/src/array/fixed_size_binary/mod.rs:193

    pub fn get(&self, i: usize) -> Option<&[u8]> {
        if !self.is_null(i) {
            // soundness: Array::is_null panics if i >= self.len
            unsafe { Some(self.value_unchecked(i)) }
        } else {
            None
        }
    }

    /// Returns a new [`FixedSizeBinaryArray`] with a different logical type.
    /// This is `O(1)`.
    /// # Panics
    /// Panics iff the dtype is not supported for the physical type.
    #[inline]
    pub fn to(self, dtype: ArrowDataType) -> Self {
        match (dtype.to_storage(), self.dtype().to_storage()) {
            (ArrowDataType::FixedSizeBinary(size_a), ArrowDataType::FixedSizeBinary(size_b))
                if size_a == size_b => {},
            _ => panic!("Wrong DataType"),
        }

        Self {
            size: self.size,
            dtype,
            values: self.values,
            validity: self.validity,
        }
    }

    /// Returns the size
    pub fn size(&self) -> usize {
        self.size
    }
}

impl FixedSizeBinaryArray {
    pub(crate) fn maybe_get_size(dtype: &ArrowDataType) -> PolarsResult<usize> {

View on GitHub (pinned to 9b5d73fd00)

Solutions

  1. Reduce the function to at most (inputs, context, kwargs); move every extra value into the kwargs struct as a field
  2. Pre-compute or close over constants outside the plugin function if the value does not need to come from Python per call
  3. Update the Python-side register_plugin_function call so the moved fields are passed in kwargs
  4. Keep using context only for CallerContext concerns (e.g. context.parallel()), not for user configuration

Example fix

// before — four parameters, count alone is rejected
#[polars_expr(output_type=Float64)]
fn dist(inputs: &[Series], context: CallerContext, kwargs: MyKwargs, factor: f64) -> PolarsResult<Series> {
    todo!()
}

// after — fold the extra value into the kwargs struct
#[derive(Deserialize)]
struct MyKwargs {
    factor: f64,
}

#[polars_expr(output_type=Float64)]
fn dist(inputs: &[Series], context: CallerContext, kwargs: MyKwargs) -> PolarsResult<Series> {
    todo!()
}
Defensive patterns

Strategy: validation

Validate before calling

# ci/check_polars_expr.py — run before cargo build
import re, sys, pathlib
pat = re.compile(r"#\[polars_expr\([^)]*\)\]\s*(?:pub\s+)?fn\s+\w+\(([^)]*)\)", re.S)
for f in pathlib.Path("src").rglob("*.rs"):
    for m in pat.finditer(f.read_text()):
        params = [p.strip() for p in m.group(1).split(",") if p.strip()]
        if len(params) > 3:
            sys.exit(f"{f}: polars_expr fn has {len(params)} parameters; max is 3 (inputs, context, kwargs). Move extras into the kwargs struct")

Prevention

When it happens

Trigger: fn f(inputs: &[Series], context: CallerContext, kwargs: MyKwargs, factor: f64) -> PolarsResult<Series>, or any 4+-parameter function under #[polars_expr(...)], even if all names after inputs are spelled correctly.

Common situations: Growing a plugin's configuration over time by appending parameters; developers used to plain Rust APIs assuming positional arguments pass through; porting a function that already took several options and trying to expose it unchanged as a Polars expression.

Related errors


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/4e7a9c6625828e19. Report an issue: GitHub.