pandas-dev/pandas · error · NotImplementedError

cannot evaluate scalar only bool ops

Error message

cannot evaluate scalar only bool ops

What it means

Raised in BinOp._disallow_scalar_only_bool_ops when at least one operand is scalar, the operator is a boolean op (&, |, and, or), and the operand types are not both bool/np.bool_. The eval engine refuses to silently coerce scalar non-bool values through Python truthiness inside a vectorized boolean expression, because the result would be ambiguous (element-wise vs scalar truth).

Solutions

  1. Convert scalar operands to bool explicitly before the eval: pd.eval('bool(1) & bool(2)') is still rejected — instead pass booleans: flag = True; df.query('@flag & (A > 0)').
  2. Use comparison operators to produce boolean scalars: df.query('(A > 0) & (B > 0)') instead of df.query('A & 1').
  3. Move scalar boolean logic out of the query string and combine in Python afterward.
  4. If the scalar is meant as a mask, broadcast it: pd.eval('(A > 0) & True') works because True is bool.

Example fix

// before
pd.eval("1 & 2")
df.query("A & 1")

// after
pd.eval("True & False")
df.query("A & (B > 0)")
Defensive patterns

Strategy: validation

Validate before calling

from pandas.core.computation.ops import _bool_ops_dict
from pandas.core.dtypes.common import is_scalar, is_bool_dtype
import numpy as np

def bool_op_safe(lhs, rhs, op: str) -> bool:
    if op not in _bool_ops_dict:
        return True
    if not (is_scalar(lhs) or is_scalar(rhs)):
        return True
    lt = type(lhs); rt = type(rhs)
    return issubclass(lt, (bool, np.bool_)) and issubclass(rt, (bool, np.bool_))

assert bool_op_safe(1, 2, '&') is False  # would raise

Type guard

import numpy as np
from typing import Any

def operands_bool_safe(lhs: Any, rhs: Any, op: str) -> bool:
    if op not in ('&', '|', 'and', 'or'):
        return True
    return isinstance(lhs, (bool, np.bool_)) and isinstance(rhs, (bool, np.bool_))

Try / catch

try:
    pd.eval('1 & 2')
except NotImplementedError as e:
    if 'scalar only bool ops' in str(e):
        pd.eval('True & False')
    else:
        raise

Prevention

When it happens

Trigger: pd.eval('1 & 2'), pd.eval('5 and 3'), df.query('A & 1') where one side is a scalar int. Any boolean operator applied between a scalar non-bool (int, float, str) and another term inside eval/query.

Common situations: Treating eval like Python and expecting short-circuit truthiness on scalars. Mixing bitwise & with integer masks inside query. Migrating from plain Python boolean expressions to df.query() without converting flags to bool.

Related errors


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

Appendix: source

Thrown at pandas/core/computation/ops.py:487

        rhs = self.rhs
        lhs = self.lhs

        # GH#24883 unwrap dtype if necessary to ensure we have a type object
        rhs_rt = rhs.return_type
        rhs_rt = getattr(rhs_rt, "type", rhs_rt)
        lhs_rt = lhs.return_type
        lhs_rt = getattr(lhs_rt, "type", lhs_rt)
        if (
            (lhs.is_scalar or rhs.is_scalar)
            and self.op in _bool_ops_dict
            and (
                not (
                    issubclass(rhs_rt, (bool, np.bool_))
                    and issubclass(lhs_rt, (bool, np.bool_))
                )
            )
        ):
            raise NotImplementedError("cannot evaluate scalar only bool ops")


UNARY_OPS_SYMS = ("+", "-", "~", "not")
_unary_ops_funcs = (operator.pos, operator.neg, operator.invert, operator.invert)
_unary_ops_dict = dict(zip(UNARY_OPS_SYMS, _unary_ops_funcs, strict=True))


class UnaryOp(Op):
    """
    Hold a unary operator and its operands.

    Parameters
    ----------
    op : str
        The token used to represent the operator.
    operand : Term or Op
        The Term or Op operand to the operator.

View on GitHub (pinned to 3b7651241d)