pola-rs/polars · error · TypeError

cannot exclude by both column name and dtype

Error message

cannot exclude by both column name and dtype

What it means

Thrown by Selector.exclude (py-polars/src/polars/selectors.py:590) when a single exclude() call receives BOTH column-name strings AND dtypes, e.g. exclude("a", pl.Int64). The implementation must build either a by-name or a by-dtype exclusion selector, so mixing the two kinds in one call is ambiguous and rejected with TypeError. Note this fires only when at least one of each kind is present in the same call.

Source

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

                if isinstance(columns, Collection) and not isinstance(columns, str)
                else [columns]
            ),
            *more_columns,
        ):
            if isinstance(item, str):
                exclude_cols.append(item)
            elif is_polars_dtype(item):
                exclude_dtypes.append(item)
            else:
                msg = (
                    "invalid input for `exclude`"
                    f"\n\nExpected one or more `str` or `DataType`; found {item!r} instead."
                )
                raise TypeError(msg)

        if exclude_cols and exclude_dtypes:
            msg = "cannot exclude by both column name and dtype"
            raise TypeError(msg)

        excluded = (
            by_dtype(exclude_dtypes)
            if exclude_dtypes
            else Selector._by_name(
                exclude_cols,
                strict=False,
                expand_patterns=True,
            )
        )
        return self - excluded

    def as_expr(self) -> Expr:
        """
        Materialize the `selector` as a normal expression.

        This ensures that the operators `|`, `&`, `~` and `-`
        are applied on the data and not on the selector sets.

View on GitHub (pinned to df599052da)

Solutions

  1. Chain two exclude calls: cs.all().exclude("foo").exclude(pl.Utf8)
  2. Or compose a single complement selector: cs.all() - (cs.by_name("foo") | cs.by_dtype(pl.Utf8))
  3. If args are dynamic, partition them first: names = [a for a in args if isinstance(a, str)]; dtypes = [a for a in args if not isinstance(a, str)], then apply each group separately

Example fix

# before
sel = cs.all().exclude("foo", pl.Utf8)

# after
sel = cs.all().exclude("foo").exclude(pl.Utf8)
# or
sel = cs.all() - (cs.by_name("foo") | cs.by_dtype(pl.Utf8))
Defensive patterns

Strategy: validation

Validate before calling

names = [a for a in args if isinstance(a, str)]
dtypes = [a for a in args if not isinstance(a, str)]
assert not (names and dtypes), "exclude: split names and dtypes into separate calls"
sel = base.exclude(*names).exclude(*dtypes)

Try / catch

try:
    sel = base.exclude(*args)
except TypeError as e:
    if "cannot exclude by both" in str(e):
        names = [a for a in args if isinstance(a, str)]
        dtypes = [a for a in args if not isinstance(a, str)]
        sel = base.exclude(*names).exclude(*dtypes)
    else:
        raise

Prevention

When it happens

Trigger: cs.all().exclude("foo", pl.Utf8), cs.numeric().exclude(["bar", pl.Float64]), or cs.all().exclude(*names, *dtypes) where names is a list of strings and dtypes a list of DataType instances.

Common situations: Dynamically building an exclusion list from two sources (user-supplied column names plus a fixed dtype list) and splatting them into one exclude() call. Refactors that merge two exclude calls into one also trip this.

Related errors


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