pola-rs/polars · error · TypeError

Expected Polars expression or object convertible to one, got

Error message

Expected Polars expression or object convertible to one, got {type(value)}.

Hint: if you tried
    group_by(by={value!r})
then you probably want to use this instead:
    group_by({value!r})

What it means

LazyFrame.group_by() only accepts Polars expressions or objects directly convertible to one (column names as strings, lists of them). This error is thrown when an unconvertible object (e.g. a list literal, dict, Series misuse, or Python value) is passed to `by`. The hint suggests the common mistake of wrapping the column name in a keyword call, e.g. group_by(by='a') instead of group_by('a').

Solutions

  1. Pass column names as positional strings or a list of strings: group_by('a') or group_by(['a','b'])
  2. Use pl.col('a') expressions instead of raw Python objects
  3. If you used the `by=` keyword with a string/list of strings, drop the keyword and pass positionally
  4. Convert foreign containers (pandas Series, numpy arrays) to Python lists or pl.Series first

Example fix

// before
df.lazy().group_by(by=['a', 'b']).agg(pl.col('x').sum())
// after
df.lazy().group_by(['a', 'b']).agg(pl.col('x').sum())
Defensive patterns

Strategy: type-guard

Validate before calling

def valid_group_keys(by):
    if isinstance(by, str):
        return True
    if isinstance(by, list):
        return all(isinstance(x, (str, pl.Expr)) for x in by)
    return isinstance(by, pl.Expr)
# call: lf.group_by(by) only if valid_group_keys(by)

Type guard

def is_polars_key(v) -> bool:
    return isinstance(v, (str, pl.Expr)) or (isinstance(v, list) and all(isinstance(x, (str, pl.Expr)) for x in v))

Try / catch

try:
    out = lf.group_by(keys).agg(...)
except TypeError as e:
    if 'Expected Polars expression' in str(e):
        keys = keys if isinstance(keys, list) else [keys]
        keys = [k if isinstance(k, (str, pl.Expr)) else str(k) for k in keys]
        out = lf.group_by(keys).agg(...)

Prevention

When it happens

Trigger: Calling df.lazy().group_by(by=<non-expression>) where `by` is not a str, pl.Expr, or list of those — e.g. passing a Python list value, a dict, a Series used as `by=<series>` in keyword position, or writing group_by(by='col') with a string in the `by` keyword where parse_into_list_of_expressions rejects it.

Common situations: Migrating from pandas groupby where a list/Series is passed; writing group_by(by=['a','b']) after seeing the signature has a `by` parameter; confusing positional/named argument usage; passing a pandas Series or numpy array directly.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at py-polars/src/polars/lazyframe/frame.py:5053

        │ a   ┆ b   ┆ c   │
        │ --- ┆ --- ┆ --- │
        │ str ┆ i64 ┆ f64 │
        ╞═════╪═════╪═════╡
        │ a   ┆ 0   ┆ 4.0 │
        │ b   ┆ 1   ┆ 3.0 │
        │ c   ┆ 1   ┆ 1.0 │
        └─────┴─────┴─────┘
        """
        for value in named_by.values():
            if not isinstance(value, (str, pl.Expr, pl.Series)):
                msg = (
                    f"Expected Polars expression or object convertible to one, got {type(value)}.\n\n"
                    "Hint: if you tried\n"
                    f"    group_by(by={value!r})\n"
                    "then you probably want to use this instead:\n"
                    f"    group_by({value!r})"
                )
                raise TypeError(msg)
        exprs = parse_into_list_of_expressions(*by, **named_by)
        lgb = self._ldf.group_by(exprs, maintain_order)
        return LazyGroupBy(lgb)

    @removed_parameters(
        RenamedParameter(
            name="by",
            new_name="group_by",
            deprecated_in="0.20.14",
            removed_in="2.0",
        )
    )
    def rolling(
        self,
        index_column: IntoExpr,
        *,
        period: str | timedelta,
        offset: str | timedelta | None = None,

View on GitHub (pinned to fe841f959e)