pandas-dev/pandas · error · ValueError

Invalid binary operator

Error message

Invalid binary operator {op!r}, valid operators are {keys}

What it means

Raised in BinOp.__init__ when the operator string is absent from _binary_ops_dict, which aggregates comparison (>, <, >=, <=, ==, !=, in, not in), boolean (&, |, and, or), and arithmetic (+, -, *, /, **, //, %) operators. The valid operator list is included in the message. This is a defensive guard; the standard parser/tokenizer only ever emits operators already in the dict, so reaching this branch usually means an internally constructed BinOp or an unsupported operator such as '@' (matmul).

Solutions

  1. Use only the supported operator set: comparison (>, <, >=, <=, ==, !=, in, not in), boolean (&, |, and, or), arithmetic (+, -, *, /, **, //, %).
  2. For bitwise XOR/shifts, perform the operation outside eval: df['A'] ^ df['B'].
  3. For matmul, use DataFrame.dot() or the @ operator in plain Python, not in a query string.
  4. If extending the engine, register your operator in _cmp_ops_dict/_bool_ops_dict/_arith_ops_dict before constructing BinOp.

Example fix

// before
from pandas.core.computation.ops import BinOp
BinOp("^", lhs, rhs)
df.query("A @ B > 1")

// after
# use vectorized op outside eval
(df['A'] ^ df['B'])
df.dot(other)
Defensive patterns

Strategy: validation

Validate before calling

from pandas.core.computation.ops import _binary_ops_dict

def is_supported_binary_op(op: str) -> bool:
    return op in _binary_ops_dict

assert is_supported_binary_op(op_token), f'unsupported binary operator: {op_token}'

Type guard

from pandas.core.computation.ops import _binary_ops_dict
from typing import Literal

SupportedBinOp = Literal['>', '<', '>=', '<=', '==', '!=', 'in', 'not in', '&', '|', 'and', 'or', '+', '-', '*', '/', '**', '//', '%']

def is_supported(op: str) -> bool:
    return op in _binary_ops_dict

Try / catch

try:
    BinOp(op, lhs, rhs)
except ValueError as e:
    if 'Invalid binary operator' in str(e):
        # fall back to python-level op
        result = lhs.value ^ rhs.value
    else:
        raise

Prevention

When it happens

Trigger: Constructing BinOp(op, lhs, rhs) directly with an operator string outside the supported set (e.g. '@', '^', '<<'). A custom subclass or monkeypatch that injects a non-standard operator token. A future/version-change parser bug that produces an unrecognized token.

Common situations: Third-party code extending the eval engine by building BinOp instances with bitwise operators not in the dict (^, <<, >>). Attempting to use the matrix-multip operator '@' inside a query string. Forks of pandas that add operators without updating _binary_ops_dict.

Related errors


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

Appendix: source

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

    lhs : Term or Op
    rhs : Term or Op
    """

    def __init__(self, op: str, lhs, rhs) -> None:
        super().__init__(op, (lhs, rhs))
        self.lhs = lhs
        self.rhs = rhs

        self._disallow_scalar_only_bool_ops()

        self.convert_values()

        try:
            self.func = _binary_ops_dict[op]
        except KeyError as err:
            # has to be made a list for python3
            keys = list(_binary_ops_dict.keys())
            raise ValueError(
                f"Invalid binary operator {op!r}, valid operators are {keys}"
            ) from err

    def __call__(self, env):
        """
        Recursively evaluate an expression in Python space.

        Parameters
        ----------
        env : Scope

        Returns
        -------
        object
            The result of an evaluated expression.
        """
        # recurse over the left/right nodes
        left = self.lhs(env)

View on GitHub (pinned to 3b7651241d)