{"record":{"id":"27cb7d62fe4e2f8e","repo":"microsoft/semantic-kernel","slug":"comparison-operator-type-operator-node-name","errorCode":null,"errorMessage":"Comparison operator '{type(operator_node).__name__}' is not allowed in filter expressions.","messagePattern":"Comparison operator '(.+?)' is not allowed in filter expressions\\.","errorType":"exception","errorClass":"VectorStoreOperationException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/in_memory.py","lineNumber":333,"sourceCode":"        if isinstance(operator_node, ast.NotEq):\n            return left != right\n        if isinstance(operator_node, ast.Lt):\n            return left < right\n        if isinstance(operator_node, ast.LtE):\n            return left <= right\n        if isinstance(operator_node, ast.Gt):\n            return left > right\n        if isinstance(operator_node, ast.GtE):\n            return left >= right\n        if isinstance(operator_node, ast.In):\n            return left in right\n        if isinstance(operator_node, ast.NotIn):\n            return left not in right\n        if isinstance(operator_node, ast.Is):\n            return left is right\n        if isinstance(operator_node, ast.IsNot):\n            return left is not right\n        raise VectorStoreOperationException(\n            f\"Comparison operator '{type(operator_node).__name__}' is not allowed in filter expressions.\"\n        )\n\n    def _safe_add(self, left: Any, right: Any) -> Any:\n        \"\"\"Safely evaluate addition.\"\"\"\n        if isinstance(left, (int, float)) and isinstance(right, (int, float)):\n            return left + right\n        if isinstance(left, str) and isinstance(right, str):\n            return self._ensure_sequence_result_size(left, right, lambda a, b: a + b)\n        if isinstance(left, list) and isinstance(right, list):\n            return self._ensure_sequence_result_size(left, right, lambda a, b: a + b)\n        if isinstance(left, tuple) and isinstance(right, tuple):\n            return self._ensure_sequence_result_size(left, right, lambda a, b: a + b)\n        raise VectorStoreOperationException(\n            \"Addition in filter expressions is only allowed for numeric values and same-type sequences.\"\n        )\n\n    def _safe_mult(self, left: Any, right: Any) -> Any:","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/in_memory.py#L315-L351","documentation":"Thrown by _compare (in_memory.py:333-335) for a comparison operator outside the handled set (Eq, NotEq, Lt, LtE, Gt, GtE, In, NotIn, Is, IsNot). Defense-in-depth: the parse-time allowlist admits exactly these comparison operators, so this branch is normally only reachable with a custom/relaxed allowlist.","triggerScenarios":"A subclass that adds an exotic comparison node type to allowed_filter_ast_nodes, or a manually constructed filter callable that bypasses parse-time checks and reaches the evaluator with an unsupported comparator.","commonSituations":"Customizing the evaluator allowlist; future Python AST node additions that are not yet handled.","solutions":["Use only the supported comparison operators in filters (==, !=, <, <=, >, >=, in, not in, is, is not).","Do not extend allowed_filter_ast_nodes with comparison node types the evaluator does not handle."],"exampleFix":"# before\n# custom allowlist admits an unsupported comparator\nlambda x: x.a <~ x.b  # not real python; a node the evaluator cannot map\n# after\nlambda x: x.a <= x.b","handlingStrategy":"validation","validationCode":"import ast\n\nSAFE_NODES = {\n    ast.Expression, ast.Lambda, ast.arguments, ast.arg, ast.Compare, ast.BoolOp,\n    ast.UnaryOp, ast.And, ast.Or, ast.Not, ast.Eq, ast.NotEq, ast.Lt, ast.LtE,\n    ast.Gt, ast.GtE, ast.In, ast.NotIn, ast.Is, ast.IsNot, ast.Name, ast.Load,\n    ast.Attribute, ast.Subscript, ast.Slice, ast.Constant, ast.List, ast.Tuple,\n    ast.Set, ast.Dict, ast.BinOp, ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod,\n    ast.FloorDiv, ast.Call,\n}\n\ndef preflight_filter(expr: str, *, max_len: int = 2048, max_nodes: int = 128) -> None:\n    if len(expr) > max_len:\n        raise ValueError(\"filter too long\")\n    try:\n        tree = ast.parse(expr, mode=\"eval\")\n    except SyntaxError as e:\n        raise ValueError(f\"invalid python: {e}\") from e\n    if not (isinstance(tree, ast.Expression) and isinstance(tree.body, ast.Lambda)):\n        raise ValueError(\"filter must be a lambda expression\")\n    blocked = {\"__class__\", \"__globals__\", \"__subclasses__\", \"__builtins__\", \"__code__\"}\n    for n in ast.walk(tree):\n        if isinstance(n, ast.Attribute) and n.attr in blocked:\n            raise ValueError(f\"blocked attribute: {n.attr}\")\n        if type(n) not in SAFE_NODES:\n            raise ValueError(f\"disallowed node: {type(n).__name__}\")\n    if sum(1 for _ in ast.walk(tree)) > max_nodes:\n        raise ValueError(\"filter too complex\")\n","typeGuard":null,"tryCatchPattern":"try:\n    results = await collection.search(search_type=SearchType.VECTOR, options=opts)\nexcept VectorStoreOperationException as e:\n    logger.warning(\"filter rejected: %s\", e.__cause__ or e)\n    results = None","preventionTips":["Stick to standard comparison operators in filter lambdas.","Re-test filters after changing the AST allowlist."],"tags":["filter","in-memory","semantic-kernel","evaluation"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}