microsoft/semantic-kernel · error · VectorStoreOperationException

Unary operator '{type(node.op).__name__}' is not allowed in

Error message

Unary operator '{type(node.op).__name__}' is not allowed in filter expressions.

What it means

_eval_UnaryOp handles only ast.Not. Any other unary operator (USub '-', UAdd '+', Invert '~') raises VectorStoreOperationException. The static allowlist only includes ast.Not among unary operators, so USub/UAdd/Invert are rejected at parse time first; this eval-time branch is the safety net if the static allowlist was widened.

Source

Thrown at python/semantic_kernel/connectors/in_memory.py:238

                result = self.evaluate(value, context)
            return result
        if isinstance(node.op, ast.Or):
            result = self.evaluate(node.values[0], context)
            for value in node.values[1:]:
                if result:
                    return result
                result = self.evaluate(value, context)
            return result
        raise VectorStoreOperationException(
            f"Boolean operator '{type(node.op).__name__}' is not allowed in filter expressions."
        )

    def _eval_UnaryOp(self, node: ast.UnaryOp, context: Mapping[str, Any]) -> Any:
        """Evaluate a unary operator."""
        operand = self.evaluate(node.operand, context)
        if isinstance(node.op, ast.Not):
            return not operand
        raise VectorStoreOperationException(
            f"Unary operator '{type(node.op).__name__}' is not allowed in filter expressions."
        )

    def _eval_Compare(self, node: ast.Compare, context: Mapping[str, Any]) -> bool:
        """Evaluate a comparison expression."""
        left = self.evaluate(node.left, context)
        for operator_node, comparator in zip(node.ops, node.comparators, strict=True):
            right = self.evaluate(comparator, context)
            if not self._compare(operator_node, left, right):
                return False
            left = right
        return True

    def _eval_BinOp(self, node: ast.BinOp, context: Mapping[str, Any]) -> Any:
        """Evaluate a binary operator."""
        left = self.evaluate(node.left, context)
        right = self.evaluate(node.right, context)

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Rewrite to avoid unary minus/plus/invert (e.g. compare against a positive literal, or use x.val * -1 only if multiplication is intended).
  2. Do not add USub/UAdd/Invert to allowed_filter_ast_nodes unless you also handle them in the evaluator.

Example fix

# before
VectorSearchOptions(filter=lambda x: -x.val > 1)  # USub -> static reject (or [1315] if allowlist widened)

# after
VectorSearchOptions(filter=lambda x: x.val < -1)  # if simple comparison is the goal, adjust the comparison instead
Defensive patterns

Strategy: validation

Validate before calling

import ast

def uses_only_not_unary(filter_str: str) -> None:
    for node in ast.walk(ast.parse(filter_str, mode='eval')):
        if isinstance(node, ast.UnaryOp) and not isinstance(node.op, ast.Not):
            raise ValueError(f"unary operator {type(node.op).__name__} is not allowed in filters")

Prevention

When it happens

Trigger: A filter like lambda x: -x.val > 1 or lambda x: ~x.flags; these reach the evaluator only if ast.USub/ast.UAdd/ast.Invert were added to allowed_filter_ast_nodes by a subclass.

Common situations: Writing arithmetic negation or bitwise-not in a filter; subclassing to allow more unary operators without an evaluator update.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/d3a56934661ad521. Report an issue: GitHub.