{"record":{"id":"2c9e09ded994eef6","repo":"pola-rs/polars","slug":"data-type-must-be-fixedsizelist-got-dtype","errorCode":null,"errorMessage":"data type must be FixedSizeList (got {dtype:?})","messagePattern":"data type must be FixedSizeList \\(got (.+?)\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/polars-arrow/src/array/fixed_size_list/mutable.rs","lineNumber":54,"sourceCode":"        let dtype = FixedSizeListArray::default_datatype(values.dtype().clone(), size);\n        Self::new_from(values, dtype, size)\n    }\n\n    /// Creates a new [`MutableFixedSizeListArray`] from a [`MutableArray`] and size.\n    pub fn new_with_field(values: M, name: PlSmallStr, nullable: bool, size: usize) -> Self {\n        let dtype = ArrowDataType::FixedSizeList(\n            Box::new(Field::new(name, values.dtype().clone(), nullable)),\n            size,\n        );\n        Self::new_from(values, dtype, size)\n    }\n\n    /// Creates a new [`MutableFixedSizeListArray`] from a [`MutableArray`], [`ArrowDataType`] and size.\n    pub fn new_from(values: M, dtype: ArrowDataType, size: usize) -> Self {\n        assert_eq!(values.len(), 0);\n        match dtype {\n            ArrowDataType::FixedSizeList(..) => (),\n            _ => panic!(\"data type must be FixedSizeList (got {dtype:?})\"),\n        };\n        Self {\n            size,\n            length: 0,\n            dtype,\n            values,\n            validity: None,\n        }\n    }\n\n    #[inline]\n    fn has_valid_invariants(&self) -> bool {\n        (self.size == 0 && self.values().len() == 0)\n            || (self.size > 0 && self.values.len() / self.size == self.length)\n    }\n\n    /// Returns the size (number of elements per slot) of this [`FixedSizeListArray`].\n    pub const fn size(&self) -> usize {","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/pola-rs/polars/blob/5d8ebabf11caea54a5c29178a64a058762f49766/crates/polars-arrow/src/array/fixed_size_list/mutable.rs#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before — attribute carries no output declaration\n#[polars_expr]\nfn double(inputs: &[Series]) -> PolarsResult<Series> {\n    todo!()\n}\n\n// after — declare the output dtype (or use output_type_func / output_type_func_with_kwargs)\n#[polars_expr(output_type=Int64)]\nfn double(inputs: &[Series]) -> PolarsResult<Series> {\n    todo!()\n}","handlingStrategy":"validation","validationCode":"# ci/check_polars_expr.py — run before cargo build\nimport re, sys, pathlib\npat = re.compile(r\"#\\[polars_expr(\\([^)]*\\))?\\]\", re.S)\nkeys = (\"output_type=\", \"output_type_func=\", \"output_type_func_with_kwargs=\")\nfor f in pathlib.Path(\"src\").rglob(\"*.rs\"):\n    text = f.read_text()\n    for m in re.finditer(r\"#\\[polars_expr[^\\]]*\\]\", text):\n        attr = m.group(0)\n        if not any(k in attr for k in keys):\n            sys.exit(f\"{f}: attribute `{attr}` declares no output type; add output_type=..., output_type_func=..., or output_type_func_with_kwargs=...\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","proc-macro","polars","compile-time","attribute-arguments"],"backgroundTag":"missing-required-macro-attribute","analyzedSha":"5d8ebabf11caea54a5c29178a64a058762f49766","analyzedAt":"2026-08-19T12:15:06.350Z","contentChangedAt":"2026-08-19T12:15:06.350Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}