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
- Remove the self/&self/&mut self/self: Type parameter so every parameter after the first is a plain typed argument
- Move the function out of the impl block: #[polars_expr] must annotate a free function, not a method
- Keep the canonical shape: first parameter inputs: &[Series] (any name), optionally followed by context: CallerContext and/or kwargs: YourKwargsStruct
- 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
- Never annotate impl-block methods with #[polars_expr]; plugin entry points are free functions
- After pasting a method body into a plugin, delete the receiver before compiling
- Keep a known-good plugin function in the crate as a template and start new ones from it
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
- Wrong DataType
- data type must be FixedSizeList (got {dtype:?})
- validity must be equal to the array's length
- offset + length overflowed
- activate 'dtype-array'
AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19).
Data as JSON: /api/errors/aba62d99c1d60ce6.
Report an issue: GitHub.