pola-rs/polars · error
data type must be FixedSizeList (got {dtype:?})
Error message
data type must be FixedSizeList (got {dtype:?}) What it means
Compile-time panic from the #[polars_expr] attribute macro in pyo3-polars-derive (lib.rs:275). After parsing the attribute arguments into attr::ExprsFunctionOptions, the macro needs one of three keys — output_type=<DataType>, output_type_func=<fn name>, or output_type_func_with_kwargs=<fn name> — to generate the schema/field function Polars calls to learn the expression's output dtype. If none was supplied (e.g. a bare #[polars_expr]), all options are None and the macro panics with "didn't understand polars_expr attribute". Declaring the output type is mandatory; there is no inference.
Source
Thrown at crates/polars-arrow/src/array/fixed_size_list/mutable.rs:54
let dtype = FixedSizeListArray::default_datatype(values.dtype().clone(), size);
Self::new_from(values, dtype, size)
}
/// Creates a new [`MutableFixedSizeListArray`] from a [`MutableArray`] and size.
pub fn new_with_field(values: M, name: PlSmallStr, nullable: bool, size: usize) -> Self {
let dtype = ArrowDataType::FixedSizeList(
Box::new(Field::new(name, values.dtype().clone(), nullable)),
size,
);
Self::new_from(values, dtype, size)
}
/// Creates a new [`MutableFixedSizeListArray`] from a [`MutableArray`], [`ArrowDataType`] and size.
pub fn new_from(values: M, dtype: ArrowDataType, size: usize) -> Self {
assert_eq!(values.len(), 0);
match dtype {
ArrowDataType::FixedSizeList(..) => (),
_ => panic!("data type must be FixedSizeList (got {dtype:?})"),
};
Self {
size,
length: 0,
dtype,
values,
validity: None,
}
}
#[inline]
fn has_valid_invariants(&self) -> bool {
(self.size == 0 && self.values().len() == 0)
|| (self.size > 0 && self.values.len() / self.size == self.length)
}
/// Returns the size (number of elements per slot) of this [`FixedSizeListArray`].
pub const fn size(&self) -> usize {View on GitHub (pinned to 5d8ebabf11)
Solutions
- Add output_type=<DataType> for a fixed dtype, e.g. #[polars_expr(output_type=Int64)] (DataType variant from polars_core)
- If the dtype depends on input dtypes, write a field function fn my_out(fields: &[Field]) -> PolarsResult<Field> and use #[polars_expr(output_type_func=my_out)]
- If that field function itself needs kwargs, use #[polars_expr(output_type_func_with_kwargs=my_out)] with fn my_out(fields: &[Field], kwargs: MyKwargs) -> PolarsResult<Field>
- Copy the exact attribute form from the example crate (example/derive_expression/expression_lib/src/expressions.rs) to avoid keyword typos
Example fix
// before — attribute carries no output declaration
#[polars_expr]
fn double(inputs: &[Series]) -> PolarsResult<Series> {
todo!()
}
// after — declare the output dtype (or use output_type_func / output_type_func_with_kwargs)
#[polars_expr(output_type=Int64)]
fn double(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(\([^)]*\))?\]", re.S)
keys = ("output_type=", "output_type_func=", "output_type_func_with_kwargs=")
for f in pathlib.Path("src").rglob("*.rs"):
text = f.read_text()
for m in re.finditer(r"#\[polars_expr[^\]]*\]", text):
attr = m.group(0)
if not any(k in attr for k in keys):
sys.exit(f"{f}: attribute `{attr}` declares no output type; add output_type=..., output_type_func=..., or output_type_func_with_kwargs=...") Prevention
- Every #[polars_expr] must declare its output: one of output_type=Dtype, output_type_func=fn, output_type_func_with_kwargs=fn
- Prefer output_type_func (with FieldsMapper) whenever the dtype depends on the inputs, so the schema stays correct
- Start from the example crate's expression_lib and keep the attribute intact when copying functions
When it happens
Trigger: Using the attribute with no arguments: #[polars_expr] on fn f(inputs: &[Series]) -> PolarsResult<Series>. Note the failure mode split: an UNKNOWN keyword inside the parens panics earlier in attr.rs:54 with "didn't recognize attribute"; this message fires specifically when the attribute is empty or contains no recognized option.
Common situations: First plugin written from a tutorial that omitted the attribute contents; stripping the attribute while debugging; assuming the return type annotation lets the macro infer the dtype (it does not — dtype is a runtime Polars concept, not the Rust return type); typos in the key name route to the sibling "didn't recognize attribute" panic instead.
Related errors
- validity must be equal to the array's length
- Wrong DataType
- validity must be equal to the array's length
- offset + length overflowed
- activate 'dtype-array'
AI-assisted analysis of pola-rs/polars@5d8ebabf11 (2026-08-19).
Data as JSON: /api/errors/2c9e09ded994eef6.
Report an issue: GitHub.