{"record":{"id":"ddbc81a3231d3896","repo":"pandas-dev/pandas","slug":"invalid-binary-operator-op-r-valid-operators-ar","errorCode":null,"errorMessage":"Invalid binary operator {op!r}, valid operators are {keys}","messagePattern":"Invalid binary operator (.+?), valid operators are (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/computation/ops.py","lineNumber":363,"sourceCode":"    lhs : Term or Op\n    rhs : Term or Op\n    \"\"\"\n\n    def __init__(self, op: str, lhs, rhs) -> None:\n        super().__init__(op, (lhs, rhs))\n        self.lhs = lhs\n        self.rhs = rhs\n\n        self._disallow_scalar_only_bool_ops()\n\n        self.convert_values()\n\n        try:\n            self.func = _binary_ops_dict[op]\n        except KeyError as err:\n            # has to be made a list for python3\n            keys = list(_binary_ops_dict.keys())\n            raise ValueError(\n                f\"Invalid binary operator {op!r}, valid operators are {keys}\"\n            ) from err\n\n    def __call__(self, env):\n        \"\"\"\n        Recursively evaluate an expression in Python space.\n\n        Parameters\n        ----------\n        env : Scope\n\n        Returns\n        -------\n        object\n            The result of an evaluated expression.\n        \"\"\"\n        # recurse over the left/right nodes\n        left = self.lhs(env)","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/computation/ops.py#L345-L381","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use only the supported operator set: comparison (>, <, >=, <=, ==, !=, in, not in), boolean (&, |, and, or), arithmetic (+, -, *, /, **, //, %).","For bitwise XOR/shifts, perform the operation outside eval: df['A'] ^ df['B'].","For matmul, use DataFrame.dot() or the @ operator in plain Python, not in a query string.","If extending the engine, register your operator in _cmp_ops_dict/_bool_ops_dict/_arith_ops_dict before constructing BinOp."],"exampleFix":"// before\nfrom pandas.core.computation.ops import BinOp\nBinOp(\"^\", lhs, rhs)\ndf.query(\"A @ B > 1\")\n\n// after\n# use vectorized op outside eval\n(df['A'] ^ df['B'])\ndf.dot(other)","handlingStrategy":"validation","validationCode":"from pandas.core.computation.ops import _binary_ops_dict\n\ndef is_supported_binary_op(op: str) -> bool:\n    return op in _binary_ops_dict\n\nassert is_supported_binary_op(op_token), f'unsupported binary operator: {op_token}'","typeGuard":"from pandas.core.computation.ops import _binary_ops_dict\nfrom typing import Literal\n\nSupportedBinOp = Literal['>', '<', '>=', '<=', '==', '!=', 'in', 'not in', '&', '|', 'and', 'or', '+', '-', '*', '/', '**', '//', '%']\n\ndef is_supported(op: str) -> bool:\n    return op in _binary_ops_dict","tryCatchPattern":"try:\n    BinOp(op, lhs, rhs)\nexcept ValueError as e:\n    if 'Invalid binary operator' in str(e):\n        # fall back to python-level op\n        result = lhs.value ^ rhs.value\n    else:\n        raise","preventionTips":["Restrict generated operators to the supported set.","If extending the engine, register custom ops in _binary_ops_dict first.","Add a unit test that asserts every operator token you emit is in _binary_ops_dict."],"tags":["pandas","eval","binary-operator","expression","defensive"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}