pola-rs/polars · error

invalid dtype: {tp!r}

Error message

invalid dtype: {tp!r}

What it means

Thrown inside cs.by_dtype (py-polars/src/polars/selectors.py:1098) when a TOP-LEVEL argument is not a DataType, not a Python class, and not a Collection. by_dtype(*dtypes) flattens collections and validates each item; an outer item that is itself invalid (an int, a string, None, a DataFrame column object, etc.) hits this else-branch and raises TypeError.

Source

Thrown at py-polars/src/polars/selectors.py:1098

    │ str   ┆ i64      │
    ╞═══════╪══════════╡
    │ bar   ┆ 5000555  │
    │ foo   ┆ -3265500 │
    └───────┴──────────┘
    """
    all_dtypes: builtins.list[PolarsDataType | PythonDataType] = []
    for tp in dtypes:
        if is_polars_dtype(tp) or isinstance(tp, type):
            all_dtypes.append(tp)
        elif isinstance(tp, Collection):
            for t in tp:
                if not (is_polars_dtype(t) or isinstance(t, type)):
                    msg = f"invalid dtype: {t!r}"
                    raise TypeError(msg)
                all_dtypes.append(t)
        else:
            msg = f"invalid dtype: {tp!r}"
            raise TypeError(msg)

    return Selector._by_dtype(all_dtypes)


def by_index(
    *indices: int | range | Sequence[int | range], require_all: bool = True
) -> Selector:
    """
    Select all columns matching the given indices (or range objects).

    Parameters
    ----------
    *indices
        One or more column indices (or range objects).
        Negative indexing is supported.
    require_all
        By default, all specified indices must be valid; if any index is out of bounds,
        an error is raised. If set to `False`, out-of-bounds indices are ignored

View on GitHub (pinned to df599052da)

Solutions

  1. Pass Polars dtypes or Python classes: cs.by_dtype(pl.Int64) or cs.by_dtype(int, float)
  2. Guard optional config: only call cs.by_dtype(*dtypes) when all items are dtypes/classes; skip or default when the list is empty
  3. Convert string dtype names to pl.* dtypes before the call (see by_dtype docs for the accepted forms)

Example fix

# before
sel = cs.by_dtype("float")  # str is not a dtype

# after
sel = cs.by_dtype(pl.Float64)
# or
sel = cs.by_dtype(float)
Defensive patterns

Strategy: type-guard

Validate before calling

import polars as pl

def to_dtype_args(items):
    out = []
    for it in items:
        if pl.api.is_polars_dtype(it) or isinstance(it, type):
            out.append(it)
        elif isinstance(it, str):
            out.append(getattr(pl, it))  # 'Int64' -> pl.Int64
        else:
            raise TypeError(f"invalid dtype: {it!r}")
    return out

Type guard

def is_by_dtype_arg(item: object) -> bool:
    import polars as pl
    return pl.api.is_polars_dtype(item) or isinstance(item, type)

Try / catch

try:
    sel = cs.by_dtype(*args)
except TypeError as e:
    if "invalid dtype" in str(e):
        # fall back to per-item validation with a clearer message
        for a in args:
            assert pl.api.is_polars_dtype(a) or isinstance(a, type), f"bad dtype {a!r}"
    raise

Prevention

When it happens

Trigger: cs.by_dtype(5), cs.by_dtype("float"), cs.by_dtype(None), or cs.by_dtype(pl.Int64, 3) where the second positional arg is a scalar non-dtype.

Common situations: Passing a single dtype name as a string ("numeric") instead of a dtype object, passing a variable that was never validated (often None from an optional config), or passing an index/position by mistake.

Related errors


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