{"record":{"id":"0ca007cb76cf7ff5","repo":"pola-rs/polars","slug":"expected-left-on-to-be-str-or-expr-got-qualifi","errorCode":null,"errorMessage":"expected `left_on` to be str or Expr, got {qualified_type_name(left_on)!r}","messagePattern":"expected `left_on` to be str or Expr, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/dataframe/frame.py","lineNumber":8225,"sourceCode":"        │ 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,\n                by_right=by_right,\n                by=by,\n                strategy=strategy,\n                suffix=suffix,","sourceCodeStart":8207,"sourceCodeEnd":8243,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/dataframe/frame.py#L8207-L8243","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nout = df.join_asof(other, left_on=['ts_left', 'id'])\n\n# after\nout = (\n    df.sort('ts_left')\n      .join_asof(other.sort('ts_right'), left_on='ts_left', right_on='ts_right', by='id')\n)","handlingStrategy":"type-guard","validationCode":"from polars.expr import Expr\n\nif on is None and not isinstance(left_on, (str, Expr)):\n    raise TypeError(f'left_on must be a single str or Expr, got {type(left_on)!r}; did you forget `on`?')","typeGuard":"from polars.expr import Expr\n\ndef is_single_asof_key(v: object) -> bool:\n    return isinstance(v, (str, Expr))","tryCatchPattern":null,"preventionTips":["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"],"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"}