pola-rs/polars · error
expected `left_on` to be str or Expr, got {qualified_type_na
Error message
expected `left_on` to be str or Expr, got {qualified_type_name(left_on)!r} What it means
In DataFrame.join_asof, when `on` is None you must specify `left_on` separately, and it must be a single str or pl.Expr. This guard fires for lists, tuples, pl.Series, numpy values — and also for None itself when `on` was forgotten entirely (the message then reports NoneType), since left_on=None alongside on=None leaves the join without any key.
Source
Thrown at py-polars/src/polars/dataframe/frame.py:8225
│ Germany ┆ 2018-08-01 ┆ 82.66 ┆ 4696 │
│ Germany ┆ 2019-01-01 ┆ 83.12 ┆ 4696 │
│ Netherlands ┆ 2016-03-01 ┆ 17.11 ┆ 784 │
│ 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,View on GitHub (pinned to df599052da)
Solutions
- Provide both single-value keys: df.join_asof(other, left_on='ts_left', right_on='ts_right')
- If the key column has the same name on both sides, use on='ts' instead of left_on/right_on
- Add extra (non-time) match keys via by=/by_left=/by_right= rather than putting them in left_on
- Ensure every value is a plain column name string or pl.col(...) — not a Series, array, or tuple
Example fix
# before
out = df.join_asof(other, left_on=['ts_left', 'id'])
# after
out = (
df.sort('ts_left')
.join_asof(other.sort('ts_right'), left_on='ts_left', right_on='ts_right', by='id')
) Defensive patterns
Strategy: type-guard
Validate before calling
from polars.expr import Expr
if on is None and not isinstance(left_on, (str, Expr)):
raise TypeError(f'left_on must be a single str or Expr, got {type(left_on)!r}; did you forget `on`?') Type guard
from polars.expr import Expr
def is_single_asof_key(v: object) -> bool:
return isinstance(v, (str, Expr)) Prevention
- Provide either on or both left_on and right_on — make the pairing explicit in call templates
- Keep multi-key needs in by=/by_left=/by_right=, never in left_on
- Static-typing note: the parameters are typed IntoExpr but only str/Expr pass runtime checks
When it happens
Trigger: df.join_asof(other, left_on=['ts', 'id']); df.join_asof(other, left_on=pl.Series(...)); df.join_asof(other, right_on='ts_right') with both on and left_on left as None; left_on=('ts',) tuple.
Common situations: Using differently named timestamp columns per side and forgetting one of the pair; assuming multi-column left_on works like DataFrame.join's; omitting all key parameters when copying a call template.
Related errors
- expected `on` to be str or Expr, got {qualified_type_name(on
- expected `right_on` to be str or Expr, got {qualified_type_n
- expected `by_predicate` to be an expression, got {qualified_
- cannot select columns using key of type {qualified_type_name
- cannot select rows using key of type {qualified_type_name(ke
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/0ca007cb76cf7ff5.
Report an issue: GitHub.