pola-rs/polars · error

expected `right_on` to be str or Expr, got {qualified_type_n

Error message

expected `right_on` to be str or Expr, got {qualified_type_name(right_on)!r}

What it means

In DataFrame.join_asof, when `on` is None the right-hand key must be given as `right_on`, and it must be a single str or pl.Expr. Passing a list, tuple, pl.Series, numpy object, or another unsupported type raises TypeError with the qualified type name. Note the checks run in order (left_on first), so this specific error means left_on already passed validation.

Source

Thrown at py-polars/src/polars/dataframe/frame.py:8228

        │ Netherlands ┆ 2018-08-01 ┆ 17.32      ┆ 910  │
        │ Netherlands ┆ 2019-01-01 ┆ 17.4       ┆ 910  │
        └─────────────┴────────────┴────────────┴──────┘
        """
        require_same_type(self, other)

        if on is not None:
            if not isinstance(on, (str, pl.Expr)):
                msg = (
                    f"expected `on` to be str or Expr, got {qualified_type_name(on)!r}"
                )
                raise TypeError(msg)
        else:
            if not isinstance(left_on, (str, pl.Expr)):
                msg = f"expected `left_on` to be str or Expr, got {qualified_type_name(left_on)!r}"
                raise TypeError(msg)
            elif not isinstance(right_on, (str, pl.Expr)):
                msg = f"expected `right_on` to be str or Expr, got {qualified_type_name(right_on)!r}"
                raise TypeError(msg)

        from polars.lazyframe.opt_flags import QueryOptFlags

        return (
            self.lazy()
            .join_asof(
                other.lazy(),
                left_on=left_on,
                right_on=right_on,
                on=on,
                by_left=by_left,
                by_right=by_right,
                by=by,
                strategy=strategy,
                suffix=suffix,
                tolerance=tolerance,
                allow_parallel=allow_parallel,
                force_parallel=force_parallel,

View on GitHub (pinned to df599052da)

Solutions

  1. Use exactly one column name or expression: df.join_asof(other, left_on='ts_left', right_on='ts_right')
  2. Move non-time match keys to by=/by_left=/by_right= instead of adding them to right_on
  3. Unpack dynamic key lists in your wrapper: right_on=keys[0] or assert len(keys) == 1

Example fix

# before
out = df.join_asof(other, left_on='ts_l', right_on=['ts_r', 'sym'])

# after
out = df.join_asof(other, left_on='ts_l', right_on='ts_r', by='sym')
Defensive patterns

Strategy: type-guard

Validate before calling

from polars.expr import Expr

if on is None:
    assert isinstance(left_on, (str, Expr)), 'left_on must be str or Expr'
    assert isinstance(right_on, (str, Expr)), 'right_on must be str or Expr'

Type guard

from polars.expr import Expr

def is_single_asof_key(v: object) -> bool:
    return isinstance(v, (str, Expr))

Prevention

When it happens

Trigger: df.join_asof(other, left_on='ts_left', right_on=['ts_right', 'id']); right_on=pl.Series(...); right_on=('ts_right',); forwarding right_on from a config that sometimes holds a list of names.

Common situations: Multi-key expectations carried over from DataFrame.join usage; parameterized join helpers where key arguments are built dynamically and may arrive as sequences; pandas merge_asof ports with left_on/right_on semantics.

Related errors


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