pola-rs/polars · error · TypeError

the `columns` argument should contain a list of all integers

Error message

the `columns` argument should contain a list of all integers or all string values

What it means

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.

Source

Thrown at py-polars/src/polars/io/_utils.py:76

    if columns is None:
        return None, None

    projection: Sequence[int] | None = None
    column_names: Sequence[str] | None = None

    if isinstance(columns, str):
        column_names = [columns]
    elif isinstance(columns, int):
        projection = [columns]
    elif is_str_sequence(columns):
        _ensure_columns_are_unique(columns)
        column_names = columns
    elif is_int_sequence(columns):
        _ensure_columns_are_unique(columns)
        projection = columns
    else:
        msg = "the `columns` argument should contain a list of all integers or all string values"
        raise TypeError(msg)

    return projection, column_names


def _ensure_columns_are_unique(columns: Sequence[str] | Sequence[int]) -> None:
    if len(columns) != len(set(columns)):
        msg = f"`columns` arg should only have unique values, got {columns!r}"
        raise ValueError(msg)


def parse_row_index_args(
    row_index_name: str | None = None,
    row_index_offset: int = 0,
) -> tuple[str, int] | None:
    """
    Parse the `row_index_name` and `row_index_offset` arguments of an I/O function.

    The Rust functions take a single tuple rather than two separate arguments.

View on GitHub (pinned to df599052da)

Solutions

  1. Make `columns` homogeneous: all strings (['a','b']) or all zero-based integer indices ([0,1])
  2. For a single column pass the bare value: columns='a' or columns=0
  3. If names and indices must be mixed, read the file (or an index-based subset) first and follow with .select(['a','b'])
  4. Validate the list before the call: all(isinstance(c, str) ...) or all(isinstance(c, int) ...)

Example fix

# before
df = pl.read_csv("f.csv", columns=[0, "b"])  # TypeError
# after
df = pl.read_csv("f.csv", columns=[0, 1])
# or
df = pl.read_csv("f.csv").select(["a", "b"])
Defensive patterns

Strategy: type-guard

Validate before calling

def check_columns_arg(columns):
    if isinstance(columns, (str, int)) and not isinstance(columns, bool):
        return
    ok = isinstance(columns, (list, tuple)) and len(columns) > 0 and (
        all(isinstance(c, str) for c in columns)
        or all(isinstance(c, int) and not isinstance(c, bool) for c in columns)
    )
    if not ok:
        raise ValueError(f"columns must be all str or all int, got {columns!r}")

Type guard

from typing import TypeGuard

def is_valid_columns_arg(columns: object) -> TypeGuard[list[str] | list[int] | str | int]:
    if isinstance(columns, (str, int)) and not isinstance(columns, bool):
        return True
    return isinstance(columns, (list, tuple)) and len(columns) > 0 and (
        all(isinstance(c, str) for c in columns)
        or all(isinstance(c, int) and not isinstance(c, bool) for c in columns)
    )

Try / catch

try:
    df = pl.read_csv(path, columns=cols)
except TypeError as e:
    if "`columns` argument" in str(e):
        raise ValueError(f"invalid columns spec {cols!r}; use all-str or all-int") from e
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/49d5dc31ff2729e7. Report an issue: GitHub.