pola-rs/polars · error · TypeError

expected a selector; found

Error message

expected a selector; found {selector!r} instead.

What it means

polars.selectors.expand_selector() requires its `selector` argument to be a genuine ColumnSelector (or, with strict=False, an expression that resolves purely to a column selection). When passed anything else — a plain string, column name list, Expr with arithmetic, etc. — it raises this TypeError instead of attempting an invalid selection.

Solutions

  1. Wrap the column name in a selector: use cs.by_name('colname') instead of the bare string.
  2. If the argument may be a plain name, convert conditionally: selector = cs.by_name(x) if isinstance(x, str) else x.
  3. For expressions involving exclude or aliasing, pass strict=False only if the expression is still a pure column selection.
  4. Verify with polars.selectors.is_selector(obj) before calling expand_selector.

Example fix

// before
cols = cs.expand_selector(df, "colx")
// after
cols = cs.expand_selector(df, cs.by_name("colx"))
Defensive patterns

Strategy: type-guard

Validate before calling

from polars.selectors import is_selector
if not is_selector(sel):
    sel = cs.by_name(sel) if isinstance(sel, str) else None
if sel is None:
    raise ValueError(f"not a selector: {sel!r}")

Type guard

def is_valid_selector(obj) -> bool:
    from polars.selectors import is_selector
    return is_selector(obj) or (hasattr(obj, 'meta') and obj.meta.is_column_selection(allow_aliasing=False))

Try / catch

try:
    cols = cs.expand_selector(df, sel)
except TypeError as e:
    if "expected a selector" in str(e):
        cols = cs.expand_selector(df, cs.by_name(sel))
    else:
        raise

Prevention

When it happens

Trigger: Calling cs.expand_selector(df, 'colname') or cs.expand_selector(df, pl.col('a') + 1) — i.e. passing a str, Expr, or other non-selector object. With strict=False, passing an Expr that aliases, renames, or computes values (meta.is_column_selection is False).

Common situations: Confusing expand_selector with select(); refactoring code where a string column name was previously accepted; dynamically built expressions that accidentally lose selector status; passing a cs.exclude(...) result into expand_selector without strict=False.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/6dbf322aa3533374. Report an issue: GitHub.

Appendix: source

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

    Allow for non-strict selection expressions (such as those
    including use of an `.exclude()` constraint) to be expanded:

    >>> cs.expand_selector(schema, cs.numeric().exclude("id"), strict=False)
    ('count', 'value')
    """
    if isinstance(target, Mapping):
        from polars.dataframe import DataFrame

        target = DataFrame(schema=target)

    if not (
        is_selector(selector)
        if strict
        else selector.meta.is_column_selection(allow_aliasing=False)
    ):
        msg = f"expected a selector; found {selector!r} instead."
        raise TypeError(msg)

    return tuple(target.select(selector).collect_schema())


# TODO: Don't use this as it collects a schema (can be very expensive for LazyFrame).
#  This should move to IR conversion / Rust.
def _expand_selectors(frame: DataFrame | LazyFrame, *items: Any) -> builtins.list[str]:
    """
    Internal function that expands any selectors to column names in the given input.

    Non-selector values are left as-is.

    Examples
    --------
    >>> from polars.selectors import _expand_selectors
    >>> import polars.selectors as cs
    >>> df = pl.DataFrame(
    ...     {

View on GitHub (pinned to fe841f959e)