{"record":{"id":"6051b4410c7a45e9","repo":"microsoft/semantic-kernel","slug":"filter-string-must-be-a-lambda-expression-e-g-l","errorCode":null,"errorMessage":"Filter string must be a lambda expression, e.g. 'lambda x: x.key == 1'","messagePattern":"Filter string must be a lambda expression, e\\.g\\. 'lambda x: x\\.key == 1'","errorType":"exception","errorClass":"VectorStoreOperationException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/in_memory.py","lineNumber":760,"sourceCode":"\n    def _parse_and_validate_filter(self, filter_str: str) -> Callable:\n        \"\"\"Parse and validate a string filter as a lambda expression, then return the callable.\n\n        Uses an allowlist approach - only explicitly permitted AST node types and function names\n        are allowed. This can be customized by overriding `allowed_filter_ast_nodes` and\n        `allowed_filter_functions` class attributes.\n        \"\"\"\n        if len(filter_str) > self.max_filter_source_length:\n            raise VectorStoreOperationException(\"Filter string exceeds the maximum allowed length.\")\n\n        try:\n            tree = ast.parse(filter_str, mode=\"eval\")\n        except SyntaxError as e:\n            raise VectorStoreOperationException(f\"Filter string is not valid Python: {e}\") from e\n\n        # Only allow lambda expressions at the top level\n        if not (isinstance(tree, ast.Expression) and isinstance(tree.body, ast.Lambda)):\n            raise VectorStoreOperationException(\n                \"Filter string must be a lambda expression, e.g. 'lambda x: x.key == 1'\"\n            )\n\n        # Get the lambda parameter name(s) to allow them as valid Name nodes\n        lambda_node = tree.body\n        lambda_param_names = {arg.arg for arg in lambda_node.args.args}\n        lambda_param_order = [arg.arg for arg in lambda_node.args.args]\n        # Walk the AST to validate all nodes against the allowlist\n        for node_count, node in enumerate(ast.walk(tree), start=1):\n            if node_count > self.max_filter_ast_node_count:\n                raise VectorStoreOperationException(\"Filter expression exceeds the maximum allowed complexity.\")\n\n            node_type = type(node)\n\n            # Check if the node type is allowed\n            if node_type not in self.allowed_filter_ast_nodes:\n                raise VectorStoreOperationException(\n                    f\"AST node type '{node_type.__name__}' is not allowed in filter expressions.\"","sourceCodeStart":742,"sourceCodeEnd":778,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/in_memory.py#L742-L778","documentation":"Thrown by _parse_and_validate_filter (in_memory.py:759-761) when the string parses successfully but the top-level node is not an ast.Lambda (e.g. a bare expression like '1+1', a function call, an assignment, or an import attempt). Only lambda expressions are accepted as the filter root.","triggerScenarios":"Passing 'x.id == 1' (no lambda wrapper), a bare expression, a statement, or an attempt like \"__import__('os')\" whose top level is a Call rather than a Lambda.","commonSituations":"Forgetting the 'lambda x:' prefix; copying an expression from another store's filter syntax; passing SQL-like or OData strings.","solutions":["Wrap the expression in a lambda, e.g. 'lambda x: x.id == 1'.","Use the lambda parameter (x) to reference the record in attribute or subscript form.","For non-lambda filter languages, translate them to a Python lambda or pass a callable."],"exampleFix":"# before\nVectorSearchOptions(filter=\"x.id == 1\")          # not a lambda\nVectorSearchOptions(filter=\"id eq 1\")             # OData, not python\n# after\nVectorSearchOptions(filter=\"lambda x: x.id == 1\")","handlingStrategy":"validation","validationCode":"import ast\ndef is_lambda_expr(expr: str) -> bool:\n    tree = ast.parse(expr, mode=\"eval\")\n    return isinstance(tree, ast.Expression) and isinstance(tree.body, ast.Lambda)","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":["Always start string filters with 'lambda x:'.","Do not pass OData/SQL filter strings to InMemoryCollection; convert to a Python lambda."],"tags":["filter","in-memory","security","validation","semantic-kernel"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}