{"record":{"id":"7ec725dc4acfd5dc","repo":"pola-rs/polars","slug":"the-truth-value-of-an-expr-is-ambiguous-you-proba","errorCode":null,"errorMessage":"the truth value of an Expr is ambiguous\n\nYou probably got here by using a Python standard library function instead of the native expressions API.\nHere are some things you might want to try:\n- instead of `pl.col('a') and pl.col('b')`, use `pl.col('a') & pl.col('b')`\n- instead of `pl.col('a') in [y, z]`, use `pl.col('a').is_in([y, z])`\n- instead of `max(pl.col('a'), pl.col('b'))`, use `pl.max_horizontal(pl.col('a'), pl.col('b'))`\n","messagePattern":"the truth value of an Expr is ambiguous\n\nYou probably got here by using a Python standard library function instead of the native expressions API\\.\nHere are some things you might want to try:\n- instead of `pl\\.col\\('a'\\) and pl\\.col\\('b'\\)`, use `pl\\.col\\('a'\\) & pl\\.col\\('b'\\)`\n- instead of `pl\\.col\\('a'\\) in \\[y, z\\]`, use `pl\\.col\\('a'\\)\\.is_in\\(\\[y, z\\]\\)`\n- instead of `max\\(pl\\.col\\('a'\\), pl\\.col\\('b'\\)\\)`, use `pl\\.max_horizontal\\(pl\\.col\\('a'\\), pl\\.col\\('b'\\)\\)`\n","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/expr/expr.py","lineNumber":332,"sourceCode":"        else:\n            return \"only during sphinx\"\n\n    def __hash__(self) -> int:\n        msg = f\"unhashable type: 'Expr'\\n\\nConsider hashing '{self}.meta'.\"\n        raise TypeError(msg)\n\n    def __bool__(self) -> NoReturn:\n        msg = (\n            \"the truth value of an Expr is ambiguous\"\n            \"\\n\\n\"\n            \"You probably got here by using a Python standard library function instead \"\n            \"of the native expressions API.\\n\"\n            \"Here are some things you might want to try:\\n\"\n            \"- instead of `pl.col('a') and pl.col('b')`, use `pl.col('a') & pl.col('b')`\\n\"\n            \"- instead of `pl.col('a') in [y, z]`, use `pl.col('a').is_in([y, z])`\\n\"\n            \"- instead of `max(pl.col('a'), pl.col('b'))`, use `pl.max_horizontal(pl.col('a'), pl.col('b'))`\\n\"\n        )\n        raise TypeError(msg)\n\n    def __abs__(self) -> Expr:\n        return self.abs()\n\n    # operators\n    def __add__(self, other: IntoExpr) -> Expr:\n        other_pyexpr = parse_into_expression(other, str_as_lit=True)\n        return wrap_expr(self._pyexpr + other_pyexpr)\n\n    def __radd__(self, other: IntoExpr) -> Expr:\n        other_pyexpr = parse_into_expression(other, str_as_lit=True)\n        return wrap_expr(other_pyexpr + self._pyexpr)\n\n    def __and__(self, other: IntoExprColumn | int | bool) -> Expr:\n        other_pyexpr = parse_into_expression(other)\n        return wrap_expr(self._pyexpr.and_(other_pyexpr))\n\n    def __rand__(self, other: IntoExprColumn | int | bool) -> Expr:","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/expr/expr.py#L314-L350","documentation":"Expr defines __bool__ to raise TypeError because a lazy expression has no truth value until it is evaluated against data. Python invokes __bool__ for `if expr:`, `and`, `or`, `not`, builtin any/all, and truthiness checks on max/min results. Polars' message lists the native replacements: &, |, is_in, and horizontal aggregate functions.","triggerScenarios":"if pl.col('a') > 5: ...; pl.col('a') and pl.col('b'); not pl.col('a').is_null(); max(pl.col('a'), pl.col('b')); assert pl.col('cnt') > 0 — all raise at expression build time.","commonSituations":"Copy-pasting pandas/pure-Python logic into lazy pipelines; using builtin max/min instead of pl.max_horizontal/pl.min_horizontal; debugging with if/print around expression objects.","solutions":["Boolean operators: use & | ~ instead of and/or/not","Membership: use .is_in([y, z]) instead of `in [y, z]`","Aggregates: use pl.max_horizontal(...) / pl.min_horizontal(...) instead of builtin max()/min()","When you genuinely need a Python branch, materialise first: value = df.select(expr).item(), then branch on value"],"exampleFix":"# before\nif pl.col('a') > 5 and pl.col('b') < 3:\n    ...  # TypeError at build time\ncond = max(pl.col('a'), pl.col('b'))\n\n# after\ndf.filter((pl.col('a') > 5) & (pl.col('b') < 3))\ncond = pl.max_horizontal(pl.col('a'), pl.col('b'))","handlingStrategy":"type-guard","validationCode":"import polars as pl\n\ndef materialise_bool(e) -> bool:\n    if isinstance(e, pl.Expr):\n        raise TypeError('cannot branch on an Expr; evaluate it first, e.g. df.select(e).item()')\n    return bool(e)","typeGuard":"import polars as pl\nfrom typing import TypeGuard\n\ndef is_polars_expr(x) -> TypeGuard[pl.Expr]:\n    return isinstance(x, pl.Expr)\n\n# before any `if x:` on dynamic values\ncolumns = [x for x in parts if not is_polars_expr(x)]","tryCatchPattern":null,"preventionTips":["Use & | ~ and .is_in() inside expressions; never and/or/not/in","Use pl.max_horizontal / pl.min_horizontal instead of builtin max/min","Materialise with df.select(expr).item() before Python control flow"],"tags":["expr","truthiness","operators","api-misuse"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}