{"record":{"id":"5f75ecf37406d579","repo":"pola-rs/polars","slug":"expected-on-to-be-str-or-expr-got-qualified-ty","errorCode":null,"errorMessage":"expected `on` to be str or Expr, got {qualified_type_name(on)!r}","messagePattern":"expected `on` to be str or Expr, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/dataframe/frame.py","lineNumber":8221,"sourceCode":"        │ ---         ┆ ---        ┆ ---        ┆ ---  │\n        │ str         ┆ date       ┆ f64        ┆ i64  │\n        ╞═════════════╪════════════╪════════════╪══════╡\n        │ Germany     ┆ 2016-03-01 ┆ 82.19      ┆ 4164 │\n        │ Germany     ┆ 2018-08-01 ┆ 82.66      ┆ 4696 │\n        │ Germany     ┆ 2019-01-01 ┆ 83.12      ┆ 4696 │\n        │ Netherlands ┆ 2016-03-01 ┆ 17.11      ┆ 784  │\n        │ Netherlands ┆ 2018-08-01 ┆ 17.32      ┆ 910  │\n        │ Netherlands ┆ 2019-01-01 ┆ 17.4       ┆ 910  │\n        └─────────────┴────────────┴────────────┴──────┘\n        \"\"\"\n        require_same_type(self, other)\n\n        if on is not None:\n            if not isinstance(on, (str, pl.Expr)):\n                msg = (\n                    f\"expected `on` to be str or Expr, got {qualified_type_name(on)!r}\"\n                )\n                raise TypeError(msg)\n        else:\n            if not isinstance(left_on, (str, pl.Expr)):\n                msg = f\"expected `left_on` to be str or Expr, got {qualified_type_name(left_on)!r}\"\n                raise TypeError(msg)\n            elif not isinstance(right_on, (str, pl.Expr)):\n                msg = f\"expected `right_on` to be str or Expr, got {qualified_type_name(right_on)!r}\"\n                raise TypeError(msg)\n\n        from polars.lazyframe.opt_flags import QueryOptFlags\n\n        return (\n            self.lazy()\n            .join_asof(\n                other.lazy(),\n                left_on=left_on,\n                right_on=right_on,\n                on=on,\n                by_left=by_left,","sourceCodeStart":8203,"sourceCodeEnd":8239,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/dataframe/frame.py#L8203-L8239","documentation":"In DataFrame.join_asof, the `on` parameter (used when both frames key on the same column) must be a single column name (str) or a pl.Expr. Unlike DataFrame.join, asof joining does not accept lists, tuples, pl.Series, or other objects for `on`, so polars validates up front and raises TypeError naming the offending type.","triggerScenarios":"df.join_asof(other, on=['time', 'id']); on=('time',); on=pl.Series(values) (e.g. timestamps computed inline); passing a numpy array or a datetime value instead of a column reference.","commonSituations":"Assuming join_asof shares DataFrame.join's multi-column `on` grammar; porting pandas merge_asof code; attempting a multi-key asof join by passing several names; confusing the key column with a value array.","solutions":["Pass a single key: df.join_asof(other, on='time') or on=pl.col('time')","For differently named keys on each side, use left_on/right_on instead of on","For a multi-key asof join, keep `on` as the single sorted time key and add by='id' (or by=['id', ...]) for the extra keys","Replace any pl.Series/numpy value with the name or pl.col(...) of the column it came from — the key must be a column, not data"],"exampleFix":"# before\nout = quotes.join_asof(trades, on=['date', 'ticker'])\n\n# after\nout = quotes.sort('date').join_asof(trades.sort('date'), on='date', by='ticker')","handlingStrategy":"type-guard","validationCode":"from polars.expr import Expr\n\nif not isinstance(on, (str, Expr)):\n    raise TypeError(f'join_asof `on` must be a single str or Expr, got {type(on)!r}')","typeGuard":"from polars.expr import Expr\n\ndef is_single_asof_key(v: object) -> bool:\n    return isinstance(v, (str, Expr))","tryCatchPattern":null,"preventionTips":["Remember join_asof grammar: one sorted time key via on (or left_on/right_on), extra keys via by","Never hand join_asof a list or Series — keys are column references, not data","Sort both frames on the key column before joining; validate its type at the same time"],"tags":["polars","dataframe","join-asof","typeerror","type-validation","time-series"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}