pola-rs/polars · error

validity must be equal to the array's length

Error message

validity must be equal to the array's length

What it means

Compile-time panic from the #[polars_expr] procedural macro in pyo3-polars-derive (create_expression_function, lib.rs:110). The macro walks the annotated function's parameters after the first one (the inputs slice) and requires every remaining parameter to be a normal typed argument (syn::FnArg::Typed). It panics with "expected a type argument" when a parameter in that position is instead a self receiver: self, &self, &mut self, mut self, or self: SomeType. syn parses receivers in any position, so such a signature reaches the macro before rustc rejects it.

Source

Thrown at crates/polars-arrow/src/array/binview/mod.rs:479

    impl_into_array!();

    /// Returns this array with a new validity.
    /// # Panic
    /// Panics iff `validity.len() != self.len()`.
    #[must_use]
    #[inline]
    pub fn with_validity(mut self, validity: Option<Bitmap>) -> Self {
        self.set_validity(validity);
        self
    }

    /// Sets the validity of this array.
    /// # Panics
    /// This function panics iff `values.len() != self.len()`.
    #[inline]
    pub fn set_validity(&mut self, validity: Option<Bitmap>) {
        if matches!(&validity, Some(bitmap) if bitmap.len() != self.len()) {
            panic!("validity must be equal to the array's length")
        }
        self.total_bytes_len.store(UNKNOWN_LEN);
        self.validity = validity;
    }

    /// Takes the validity of this array, leaving it without a validity mask.
    #[inline]
    pub fn take_validity(&mut self) -> Option<Bitmap> {
        self.total_bytes_len.store(UNKNOWN_LEN);
        self.validity.take()
    }

    pub fn from_slice<S: AsRef<T>, P: AsRef<[Option<S>]>>(slice: P) -> Self {
        let mutable = MutableBinaryViewArray::from_iterator(
            slice.as_ref().iter().map(|opt_v| opt_v.as_ref()),
        );
        mutable.into()
    }

View on GitHub (pinned to 9b5d73fd00)

Solutions

  1. Remove the self/&self/&mut self/self: Type parameter so every parameter after the first is a plain typed argument
  2. Move the function out of the impl block: #[polars_expr] must annotate a free function, not a method
  3. Keep the canonical shape: first parameter inputs: &[Series] (any name), optionally followed by context: CallerContext and/or kwargs: YourKwargsStruct
  4. Run cargo check on the plugin crate to confirm the macro expands cleanly before writing the Python registration side

Example fix

// before — receiver left in the parameter list
#[polars_expr(output_type=Int64)]
fn my_expr(inputs: &[Series], &self) -> PolarsResult<Series> {
    todo!()
}

// after — free function with plain typed parameters only
#[polars_expr(output_type=Int64)]
fn my_expr(inputs: &[Series]) -> 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 any("self" in p.split(":")[0] for p in params[1:]):
            sys.exit(f"{f}: polars_expr fn has a `self` receiver in its parameters: {params}")

Prevention

When it happens

Trigger: Annotating a function with #[polars_expr] where any parameter after the first is a receiver, e.g. fn my_expr(inputs: &[Series], &self, kwargs: MyKwargs) -> PolarsResult<Series>, or fn my_expr(inputs: &[Series], self: PluginCtx). Typically happens when an impl-block method body is pasted under the macro without stripping the receiver, or when converting a method-based API into a Polars plugin entry point.

Common situations: Porting a method from an impl block into an expression plugin crate; copy-pasting example code that used methods; refactoring older pyo3-polars plugin code where the calling convention differed; misunderstanding that #[polars_expr] must sit on a free function, not a method.

Related errors


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