{"record":{"id":"49d5dc31ff2729e7","repo":"pola-rs/polars","slug":"the-columns-argument-should-contain-a-list-of-al","errorCode":null,"errorMessage":"the `columns` argument should contain a list of all integers or all string values","messagePattern":"the `columns` argument should contain a list of all integers or all string values","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/_utils.py","lineNumber":76,"sourceCode":"    if columns is None:\n        return None, None\n\n    projection: Sequence[int] | None = None\n    column_names: Sequence[str] | None = None\n\n    if isinstance(columns, str):\n        column_names = [columns]\n    elif isinstance(columns, int):\n        projection = [columns]\n    elif is_str_sequence(columns):\n        _ensure_columns_are_unique(columns)\n        column_names = columns\n    elif is_int_sequence(columns):\n        _ensure_columns_are_unique(columns)\n        projection = columns\n    else:\n        msg = \"the `columns` argument should contain a list of all integers or all string values\"\n        raise TypeError(msg)\n\n    return projection, column_names\n\n\ndef _ensure_columns_are_unique(columns: Sequence[str] | Sequence[int]) -> None:\n    if len(columns) != len(set(columns)):\n        msg = f\"`columns` arg should only have unique values, got {columns!r}\"\n        raise ValueError(msg)\n\n\ndef parse_row_index_args(\n    row_index_name: str | None = None,\n    row_index_offset: int = 0,\n) -> tuple[str, int] | None:\n    \"\"\"\n    Parse the `row_index_name` and `row_index_offset` arguments of an I/O function.\n\n    The Rust functions take a single tuple rather than two separate arguments.","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/_utils.py#L58-L94","documentation":"polars' parse_columns_arg (py-polars/src/polars/io/_utils.py:60-78) normalizes the `columns` argument of eager readers such as read_csv/read_parquet/read_ipc. It accepts exactly one of: a single str, a single int, a sequence of all strs, or a sequence of all ints. Anything else - a mixed list like [0, 'b'], a set, a dict, or a list containing None - falls into the final else branch and raises this TypeError before any file is opened.","triggerScenarios":"pl.read_csv('f.csv', columns=[0, 'b']) with mixed int/str entries; columns={'a','b'} (a set, not a list/tuple); columns=[None]; passing a name-to-index dict; same shapes in read_parquet/read_ipc/read_avro which all route through parse_columns_arg.","commonSituations":"Dynamic column selection where indices and names come from different sources; copying the output of a set comprehension into `columns`; config-driven ETL jobs where the user-supplied column spec is never pre-validated.","solutions":["Make `columns` homogeneous: all strings (['a','b']) or all zero-based integer indices ([0,1])","For a single column pass the bare value: columns='a' or columns=0","If names and indices must be mixed, read the file (or an index-based subset) first and follow with .select(['a','b'])","Validate the list before the call: all(isinstance(c, str) ...) or all(isinstance(c, int) ...)"],"exampleFix":"# before\ndf = pl.read_csv(\"f.csv\", columns=[0, \"b\"])  # TypeError\n# after\ndf = pl.read_csv(\"f.csv\", columns=[0, 1])\n# or\ndf = pl.read_csv(\"f.csv\").select([\"a\", \"b\"])","handlingStrategy":"type-guard","validationCode":"def check_columns_arg(columns):\n    if isinstance(columns, (str, int)) and not isinstance(columns, bool):\n        return\n    ok = isinstance(columns, (list, tuple)) and len(columns) > 0 and (\n        all(isinstance(c, str) for c in columns)\n        or all(isinstance(c, int) and not isinstance(c, bool) for c in columns)\n    )\n    if not ok:\n        raise ValueError(f\"columns must be all str or all int, got {columns!r}\")","typeGuard":"from typing import TypeGuard\n\ndef is_valid_columns_arg(columns: object) -> TypeGuard[list[str] | list[int] | str | int]:\n    if isinstance(columns, (str, int)) and not isinstance(columns, bool):\n        return True\n    return isinstance(columns, (list, tuple)) and len(columns) > 0 and (\n        all(isinstance(c, str) for c in columns)\n        or all(isinstance(c, int) and not isinstance(c, bool) for c in columns)\n    )","tryCatchPattern":"try:\n    df = pl.read_csv(path, columns=cols)\nexcept TypeError as e:\n    if \"`columns` argument\" in str(e):\n        raise ValueError(f\"invalid columns spec {cols!r}; use all-str or all-int\") from e\n    raise","preventionTips":["Keep column specs in one vocabulary (names or indices) end-to-end in the pipeline","Freeze user-supplied column lists to all-str or all-int before calling polars","Never pass a set, dict, or generator as `columns`"],"tags":["polars","io","columns","typeerror","validation"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}