{"record":{"id":"fa58fa65d9106e0e","repo":"pola-rs/polars","slug":"python-function-should-return-list-str","errorCode":null,"errorMessage":"python function should return List[str]","messagePattern":"python function should return List\\[str\\]","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/polars-python/src/lazyframe/general.rs","lineNumber":291,"sourceCode":"            .with_raise_if_empty(raise_if_empty)\n            .with_include_file_paths(include_file_paths.map(|x| x.into()))\n            .with_extra_columns_policy(extra_columns.0)\n            .with_missing_columns_policy(missing_columns.map(|x| x.0));\n\n        if let Some(new_columns) = new_columns {\n            r = r.with_column_names_overwrite(new_columns.0);\n        }\n\n        if let Some(lambda) = with_schema_modify {\n            let f = |schema: Schema| {\n                let iter = schema.iter_names().map(|s| s.as_str());\n                Python::attach(|py| {\n                    let names = PyList::new(py, iter).unwrap();\n\n                    let out = lambda.call1(py, (names,)).expect(\"python function failed\");\n                    let new_names = out\n                        .extract::<Vec<String>>(py)\n                        .expect(\"python function should return List[str]\");\n                    polars_ensure!(new_names.len() == schema.len(),\n                        ShapeMismatch: \"The length of the new names list should be equal to or less than the original column length\",\n                    );\n                    Ok(schema\n                        .iter_values()\n                        .zip(new_names)\n                        .map(|(dtype, name)| Field::new(name.into(), dtype.clone()))\n                        .collect())\n                })\n            };\n            r = r.with_schema_modify(f).map_err(PyPolarsErr::from)?\n        }\n\n        Ok(r.finish().map_err(PyPolarsErr::from)?.into())\n    }\n\n    #[cfg(feature = \"parquet\")]\n    #[staticmethod]","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/pola-rs/polars/blob/fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e/crates/polars-python/src/lazyframe/general.rs#L273-L309","documentation":"In the same with_schema_modify flow, polars extracts the lambda's return value as Vec<String>. `expect(\"python function should return List[str]\")` panics when the callback returns something that is not a list of strings (e.g. a single string, a dict, a tuple, None, or a list containing non-string items).","triggerScenarios":"pl.read_csv/scan_csv with with_schema_modify whose lambda returns None, a dict (name->name map), a generator, a tuple, or a list containing non-str elements.","commonSituations":"Users returning a dict for renaming (dict-style thinking), returning the input list unmodified but as a different type, accidentally returning a pandas Index or numpy array of names.","solutions":["Return a plain Python list of str: lambda names: [n.lower() for n in names].","If you have a dict mapping, convert: lambda names: [mapping.get(n, n) for n in names].","Convert numpy/pandas name collections: lambda names: [str(n) for n in list(names_obj)].","Ensure every element is str (e.g. wrap with str(n) if names may include other types)."],"exampleFix":"# before\npl.scan_csv(path, with_schema_modify=lambda names: {n: n.lower() for n in names})  # dict -> panic\n# after\npl.scan_csv(path, with_schema_modify=lambda names: [n.lower() for n in names])  # List[str]","handlingStrategy":"type-guard","validationCode":"result = fn(['a', 'b'])\nassert isinstance(result, list) and all(isinstance(x, str) for x in result)","typeGuard":"def is_list_of_str(v):\n    return isinstance(v, list) and all(isinstance(x, str) for x in v)","tryCatchPattern":"try:\n    df = pl.read_csv(path, with_schema_modify=my_renamer)\nexcept BaseException as e:\n    if 'List[str]' in str(e):\n        raise TypeError('with_schema_modify must return List[str]') from e\n    raise","preventionTips":["Always return a plain Python list of str from the renamer.","Convert dicts/arrays: [mapping.get(n, n) for n in names].","Coerce elements with str(n) if sources may be non-string."],"tags":["python","callback","csv","type-mismatch","panic"],"backgroundTag":"type-mismatch","analyzedSha":"fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e","analyzedAt":"2026-09-18T22:14:11.667Z","contentChangedAt":"2026-09-18T22:14:11.667Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}