pandas-dev/pandas · error · ValueError
Invalid unary operator {op!r}, valid operators are {UNARY_OP
Error message
Invalid unary operator {op!r}, valid operators are {UNARY_OPS_SYMS} What it means
Raised by UnaryOp.__init__ in pandas.core.computation.ops when the operator token is not in _unary_ops_dict, whose keys are UNARY_OPS_SYMS = ('+','-','~','not'). It is a ValueError chained from the underlying KeyError. Because the AST tokenizer only ever produces these four unary forms, this is effectively an internal invariant guard rather than something reachable from a normal pd.eval/df.query string.
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_dictView on GitHub (pinned to 71959b8cb9)
Solutions
- Only use '+', '-', '~', or 'not' as unary operators in eval expressions.
- Replace '!' with 'not ' (e.g. pd.eval('not (a > 0)')).
- If calling UnaryOp directly, validate the token against UNARY_OPS_SYMS before constructing.
Example fix
# before
from pandas.core.computation.ops import UnaryOp, Term
UnaryOp('!', term) # ValueError
# after (in an expression)
import pandas as pd
pd.eval('not (a > 0)') # use Python 'not'
# after (bitwise NOT on integers/bools)
pd.eval('~b') # b must be bool or int Defensive patterns
Strategy: validation
Validate before calling
from pandas.core.computation.ops import UNARY_OPS_SYMS
def assert_unary_op(op: str) -> str:
if op not in UNARY_OPS_SYMS:
raise ValueError(f'{op!r} not a valid unary op; use {UNARY_OPS_SYMS}')
return op Type guard
from pandas.core.computation.ops import UNARY_OPS_SYMS
def is_supported_unary_op(op: str) -> bool:
return op in UNARY_OPS_SYMS
Try / catch
try:
UnaryOp(op, operand)
except ValueError as e:
if 'Invalid unary operator' in str(e):
# remap to a supported token or skip eval
...
raise Prevention
- Use only '+', '-', '~', 'not' as unary operators in expressions.
- Replace '!' with 'not'.
- Avoid constructing UnaryOp manually.
When it happens
Trigger: Constructing ops.UnaryOp directly with an unsupported token (e.g. UnaryOp('!', term)), or a custom engine emitting an exotic unary token. Public eval expressions cannot reach this path because the parser would have rejected the token at parse time.
Common situations: Third-party libraries or experimental code that builds Op trees by hand; users assuming C/JS-style '!' or 'not()' syntax is honored. Python's `not` IS supported, but '!' is not.
Related errors
- Invalid binary operator {op!r}, valid operators are {keys}
- keyword error in function call '{node.func.id}'
- No accumulation for {func} implemented on BaseMaskedArray
- No accumulation for {func} implemented on BaseMaskedArray
- Could not infer freq from start/end
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/d9e047b201fa28b2.
Report an issue: GitHub.