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__ when Python tries to coerce a pandas.api.typing.Expression (produced by pd.col(...)) into a single bool. Expression represents a deferred column operation and evaluates to a per-row Series, so a single truth value is undefined (like NumPy's ambiguous-truth error). The method is typed NoReturn so any bool(ctx) usage fails.

Solutions

  1. Use the Expression where it will be evaluated against a DataFrame: df.loc[pd.col('a') > 0].
  2. If you need a scalar test, evaluate first: result = pd.col('a')._eval_expression(df); then test result.
  3. Replace `if expr:` with the boolean reduction you actually want (e.g. (expr).any()).

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.api.typing import Expression
if isinstance(x, Expression):
    raise TypeError('Expression must be evaluated against a DataFrame before bool()')

Type guard

def is_expression(x) -> bool:
    from pandas.api.typing import Expression
    return isinstance(x, Expression)

Prevention

When it happens

Trigger: if pd.col('a'): ...; bool(pd.col('a') > 0); using an Expression directly as the test in assert or `and`/`or`; passing an Expression to a function that calls bool() on it (e.g. filter()).

Common situations: Treating pd.col('x') like a scalar in a Python conditional; building an Expression inside an if instead of passing it to .loc/.assign/.query where it is evaluated against a DataFrame.

Related errors


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

Appendix: 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 3b7651241d)