{"record":{"id":"797fbb515de78e2f","repo":"pola-rs/polars","slug":"expected-right-on-to-be-str-or-expr-got-qualif","errorCode":null,"errorMessage":"expected `right_on` to be str or Expr, got {qualified_type_name(right_on)!r}","messagePattern":"expected `right_on` to be str or Expr, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/dataframe/frame.py","lineNumber":8228,"sourceCode":"        │ 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,\n                by_right=by_right,\n                by=by,\n                strategy=strategy,\n                suffix=suffix,\n                tolerance=tolerance,\n                allow_parallel=allow_parallel,\n                force_parallel=force_parallel,","sourceCodeStart":8210,"sourceCodeEnd":8246,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/dataframe/frame.py#L8210-L8246","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use exactly one column name or expression: df.join_asof(other, left_on='ts_left', right_on='ts_right')","Move non-time match keys to by=/by_left=/by_right= instead of adding them to right_on","Unpack dynamic key lists in your wrapper: right_on=keys[0] or assert len(keys) == 1"],"exampleFix":"# before\nout = df.join_asof(other, left_on='ts_l', right_on=['ts_r', 'sym'])\n\n# after\nout = df.join_asof(other, left_on='ts_l', right_on='ts_r', by='sym')","handlingStrategy":"type-guard","validationCode":"from polars.expr import Expr\n\nif on is None:\n    assert isinstance(left_on, (str, Expr)), 'left_on must be str or Expr'\n    assert isinstance(right_on, (str, Expr)), 'right_on must be str or Expr'","typeGuard":"from polars.expr import Expr\n\ndef is_single_asof_key(v: object) -> bool:\n    return isinstance(v, (str, Expr))","tryCatchPattern":null,"preventionTips":["Build asof key pairs as single names/exprs only; unpack sequences with keys[0] plus an assertion","Validate both left_on and right_on together in one guard before calling","Cover join_asof calls in type-checked code (mypy) — the runtime check is a backstop"],"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"}