pandas-dev/pandas · error · TypeError

boolean value of an expression is ambiguous

Error message

boolean value of an expression is ambiguous

What it means

Raised by Expression.__bool__ (pandas/core/col.py:357). `pd.col(name)` returns a deferred Expression representing a not-yet-bound column; its truth value is undefined because the column does not exist until evaluated against a DataFrame. Python calls __bool__ whenever an object is used in `if`, `and`, `or`, `not`, or ternary contexts, so this guard prevents ambiguous boolean coercion.

Source

Thrown at pandas/core/col.py:357

            evaluated = []
            for condition, replacement in caselist:
                if isinstance(condition, Expression):
                    condition = condition._eval_expression(df)
                if isinstance(replacement, Expression):
                    replacement = replacement._eval_expression(df)
                evaluated.append((condition, replacement))
            return ser.case_when(evaluated)

        # Keep repr compact; caselist may be large.
        repr_str = f"{self!r}.case_when(...)"
        return Expression(func, repr_str)

    def __repr__(self) -> str:
        return self._repr_str or "Expr(...)"

    # Unsupported ops
    def __bool__(self) -> NoReturn:
        raise TypeError("boolean value of an expression is ambiguous")

    def __iter__(self) -> NoReturn:
        raise TypeError("Expression objects are not iterable")

    def __copy__(self) -> NoReturn:
        raise TypeError("Expression objects are not copiable")

    def __deepcopy__(self, memo: dict[int, Any] | None) -> NoReturn:
        raise TypeError("Expression objects are not copiable")


@set_module("pandas")
def col(col_name: Hashable) -> Expression:
    """
    Generate deferred object representing a column of a DataFrame.

    Any place which accepts ``lambda df: df[col_name]``, such as
    :meth:`DataFrame.assign` or :meth:`DataFrame.loc`, can also accept

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Use the Expression only inside a context that evaluates it against a DataFrame: `df.assign(flag=pd.col('x') > 5)` or `df.loc[pd.col('x') > 5]`.
  2. If you need a scalar boolean, first bind the expression to a frame and reduce: `bool((df['x'] > 5).any())`.
  3. Rewrite `if expr:` logic to operate on the resolved Series after evaluation.

Example fix

# before
expr = pd.col('speed') > 100
if expr:
    ...

# after
df = df.assign(fast=pd.col('speed') > 100)
Defensive patterns

Strategy: type-guard

Validate before calling

from pandas.core.col import Expression

def assert_evaluable(expr):
    if isinstance(expr, Expression):
        raise TypeError("Expression cannot be used in a boolean context; evaluate against a DataFrame first")

Type guard

from pandas.core.col import Expression

def is_expression(obj) -> bool:
    return isinstance(obj, Expression)

Try / catch

from pandas.core.col import Expression

try:
    result = bool(obj)
except TypeError as e:
    if 'ambiguous' in str(e) and isinstance(obj, Expression):
        # evaluate against a frame and reduce instead
        result = bool(obj._eval_expression(df).any())
    else:
        raise

Prevention

When it happens

Trigger: Writing `if pd.col('x') > 5:` (the comparison returns an Expression, not a bool), `pd.col('x') and pd.col('y')`, or `bool(pd.col('x'))`. Also `~pd.col('x')` inside an `if`, or passing an Expression to a function that does `if value:`.

Common situations: Treating a deferred Expression like a concrete value. Using `pd.col` inside conditional logic instead of inside assign/loc/query which evaluate the expression against a DataFrame.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/c2f06d9fe05a5196. Report an issue: GitHub.