pandas-dev/pandas · error · ValueError

Invalid unary operator

Error message

Invalid unary operator {op!r}, valid operators are {UNARY_OPS_SYMS}

What it means

Raised in UnaryOp.__init__ when the operator string is not in _unary_ops_dict, whose keys are '+', '-', '~', 'not' (UNARY_OPS_SYMS). The valid set is printed in the message. Like the binary counterpart, this is a defensive guard: the parser only emits those four unary forms, so hitting it implies an internally constructed UnaryOp or an unsupported token such as unary '*'.

Solutions

  1. Restrict unary usage to '+', '-', '~', 'not' inside eval/query expressions.
  2. For unsupported unary forms (e.g. logical not on non-bool), rewrite using bitwise/comparison operators: df.query('~(A > 0)') instead of df.query('not (A > 0)') if 'not' is problematic.
  3. If extending the engine, register the operator in _unary_ops_funcs/_unary_ops_dict before constructing UnaryOp.

Example fix

// before
from pandas.core.computation.ops import UnaryOp
UnaryOp("!", operand)

// after
UnaryOp("~", operand)
Defensive patterns

Strategy: validation

Validate before calling

from pandas.core.computation.ops import _unary_ops_dict

def is_supported_unary_op(op: str) -> bool:
    return op in _unary_ops_dict

assert is_supported_unary_op(op_token)

Type guard

from typing import Literal

SupportedUnaryOp = Literal['+', '-', '~', 'not']

def is_supported(op: str) -> bool:
    return op in {'+', '-', '~', 'not'}

Try / catch

try:
    UnaryOp(op, operand)
except ValueError as e:
    if 'Invalid unary operator' in str(e):
        UnaryOp('~', operand)  # fallback to invert
    else:
        raise

Prevention

When it happens

Trigger: Directly constructing UnaryOp(op, operand) with an operator outside ('+', '-', '~', 'not'). Custom code paths that feed an unrecognized unary token into the eval AST visitor.

Common situations: Forks/extensions trying to add new unary operators. A parser regression that produces an unexpected unary node. Misuse of internal API by downstream libraries building Op trees by hand.

Related errors


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

Appendix: source

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

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

    Raises
    ------
    ValueError
        * If no function associated with the passed operator token is found.
    """

    def __init__(self, op: Literal["+", "-", "~", "not"], operand) -> None:
        super().__init__(op, (operand,))
        self.operand = operand

        try:
            self.func = _unary_ops_dict[op]
        except KeyError as err:
            raise ValueError(
                f"Invalid unary operator {op!r}, valid operators are {UNARY_OPS_SYMS}"
            ) from err

    def __call__(self, env) -> MathCall:
        operand = self.operand(env)
        # error: Cannot call function of unknown type
        return self.func(operand)  # type: ignore[operator]

    def __repr__(self) -> str:
        return pprint_thing(f"{self.op}({self.operand})")

    @property
    def return_type(self) -> np.dtype:
        operand = self.operand
        if operand.return_type == np.dtype("bool"):
            return np.dtype("bool")
        if isinstance(operand, Op) and (
            operand.op in _cmp_ops_dict or operand.op in _bool_ops_dict

View on GitHub (pinned to 3b7651241d)