{"record":{"id":"d3a56934661ad521","repo":"microsoft/semantic-kernel","slug":"unary-operator-type-node-op-name-is-not-a","errorCode":null,"errorMessage":"Unary operator '{type(node.op).__name__}' is not allowed in filter expressions.","messagePattern":"Unary operator '(.+?)' is not allowed in filter expressions\\.","errorType":"exception","errorClass":"VectorStoreOperationException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/in_memory.py","lineNumber":238,"sourceCode":"                result = self.evaluate(value, context)\n            return result\n        if isinstance(node.op, ast.Or):\n            result = self.evaluate(node.values[0], context)\n            for value in node.values[1:]:\n                if result:\n                    return result\n                result = self.evaluate(value, context)\n            return result\n        raise VectorStoreOperationException(\n            f\"Boolean operator '{type(node.op).__name__}' is not allowed in filter expressions.\"\n        )\n\n    def _eval_UnaryOp(self, node: ast.UnaryOp, context: Mapping[str, Any]) -> Any:\n        \"\"\"Evaluate a unary operator.\"\"\"\n        operand = self.evaluate(node.operand, context)\n        if isinstance(node.op, ast.Not):\n            return not operand\n        raise VectorStoreOperationException(\n            f\"Unary operator '{type(node.op).__name__}' is not allowed in filter expressions.\"\n        )\n\n    def _eval_Compare(self, node: ast.Compare, context: Mapping[str, Any]) -> bool:\n        \"\"\"Evaluate a comparison expression.\"\"\"\n        left = self.evaluate(node.left, context)\n        for operator_node, comparator in zip(node.ops, node.comparators, strict=True):\n            right = self.evaluate(comparator, context)\n            if not self._compare(operator_node, left, right):\n                return False\n            left = right\n        return True\n\n    def _eval_BinOp(self, node: ast.BinOp, context: Mapping[str, Any]) -> Any:\n        \"\"\"Evaluate a binary operator.\"\"\"\n        left = self.evaluate(node.left, context)\n        right = self.evaluate(node.right, context)\n","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/in_memory.py#L220-L256","documentation":"_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.","triggerScenarios":"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.","commonSituations":"Writing arithmetic negation or bitwise-not in a filter; subclassing to allow more unary operators without an evaluator update.","solutions":["Rewrite to avoid unary minus/plus/invert (e.g. compare against a positive literal, or use x.val * -1 only if multiplication is intended).","Do not add USub/UAdd/Invert to allowed_filter_ast_nodes unless you also handle them in the evaluator."],"exampleFix":"# before\nVectorSearchOptions(filter=lambda x: -x.val > 1)  # USub -> static reject (or [1315] if allowlist widened)\n\n# after\nVectorSearchOptions(filter=lambda x: x.val < -1)  # if simple comparison is the goal, adjust the comparison instead","handlingStrategy":"validation","validationCode":"import ast\n\ndef uses_only_not_unary(filter_str: str) -> None:\n    for node in ast.walk(ast.parse(filter_str, mode='eval')):\n        if isinstance(node, ast.UnaryOp) and not isinstance(node.op, ast.Not):\n            raise ValueError(f\"unary operator {type(node.op).__name__} is not allowed in filters\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid unary minus/plus/invert in filters; restructure the comparison.","Do not add USub/UAdd/Invert to allowed_filter_ast_nodes."],"tags":["in-memory","filter","ast"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}