microsoft/semantic-kernel · error · VectorStoreOperationException

AST node type '{node_type.__name__}' is not allowed in filte

Error message

AST node type '{node_type.__name__}' is not allowed in filter expressions.

What it means

Thrown by _parse_and_validate_filter (in_memory.py:776-778) when the filter contains an AST node type not in allowed_filter_ast_nodes. Common offenders: ternary IfExp, comprehensions (ListComp/SetComp/DictComp/GeneratorExp), f-strings (JoinedStr/FormattedValue), Starred, walrus NamedExpr, Await, and Yield.

Source

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

        if not (isinstance(tree, ast.Expression) and isinstance(tree.body, ast.Lambda)):
            raise VectorStoreOperationException(
                "Filter string must be a lambda expression, e.g. 'lambda x: x.key == 1'"
            )

        # Get the lambda parameter name(s) to allow them as valid Name nodes
        lambda_node = tree.body
        lambda_param_names = {arg.arg for arg in lambda_node.args.args}
        lambda_param_order = [arg.arg for arg in lambda_node.args.args]
        # Walk the AST to validate all nodes against the allowlist
        for node_count, node in enumerate(ast.walk(tree), start=1):
            if node_count > self.max_filter_ast_node_count:
                raise VectorStoreOperationException("Filter expression exceeds the maximum allowed complexity.")

            node_type = type(node)

            # Check if the node type is allowed
            if node_type not in self.allowed_filter_ast_nodes:
                raise VectorStoreOperationException(
                    f"AST node type '{node_type.__name__}' is not allowed in filter expressions."
                )

            # For Attribute nodes, validate that dangerous dunder attributes are not accessed
            if isinstance(node, ast.Attribute) and node.attr in self.blocked_filter_attributes:
                raise VectorStoreOperationException(
                    f"Access to attribute '{node.attr}' is not allowed in filter expressions. "
                    "This attribute could be used to escape the filter sandbox."
                )

            # For Name nodes, only allow the lambda parameter
            if isinstance(node, ast.Name) and node.id not in lambda_param_names:
                raise VectorStoreOperationException(
                    f"Use of name '{node.id}' is not allowed in filter expressions. "
                    f"Only the lambda parameter(s) ({', '.join(lambda_param_names)}) can be used."
                )

            # For Call nodes, validate that only allowed functions are called

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Rewrite using only allowed constructs: comparisons, boolean ops (and/or/not), calls to allowlisted functions, literals, basic arithmetic, subscript, and attribute access.
  2. Replace a ternary with boolean short-circuit: `(c and a) or b`.
  3. Replace a comprehension with an allowlisted builtin call such as any()/all() over a literal.
  4. Precompute complex values outside the filter and pass them via a callable closure.

Example fix

# before
VectorSearchOptions(filter="lambda x: 'a' if x.f else 'b'")      # IfExp not allowed
# after
VectorSearchOptions(filter="lambda x: (x.f and 'a') or 'b'")        # boolean short-circuit
Defensive patterns

Strategy: validation

Validate before calling

import ast

SAFE_NODES = {
    ast.Expression, ast.Lambda, ast.arguments, ast.arg, ast.Compare, ast.BoolOp,
    ast.UnaryOp, ast.And, ast.Or, ast.Not, ast.Eq, ast.NotEq, ast.Lt, ast.LtE,
    ast.Gt, ast.GtE, ast.In, ast.NotIn, ast.Is, ast.IsNot, ast.Name, ast.Load,
    ast.Attribute, ast.Subscript, ast.Slice, ast.Constant, ast.List, ast.Tuple,
    ast.Set, ast.Dict, ast.BinOp, ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod,
    ast.FloorDiv, ast.Call,
}

def preflight_filter(expr: str, *, max_len: int = 2048, max_nodes: int = 128) -> None:
    if len(expr) > max_len:
        raise ValueError("filter too long")
    try:
        tree = ast.parse(expr, mode="eval")
    except SyntaxError as e:
        raise ValueError(f"invalid python: {e}") from e
    if not (isinstance(tree, ast.Expression) and isinstance(tree.body, ast.Lambda)):
        raise ValueError("filter must be a lambda expression")
    blocked = {"__class__", "__globals__", "__subclasses__", "__builtins__", "__code__"}
    for n in ast.walk(tree):
        if isinstance(n, ast.Attribute) and n.attr in blocked:
            raise ValueError(f"blocked attribute: {n.attr}")
        if type(n) not in SAFE_NODES:
            raise ValueError(f"disallowed node: {type(n).__name__}")
    if sum(1 for _ in ast.walk(tree)) > max_nodes:
        raise ValueError("filter too complex")

Try / catch

try:
    results = await collection.search(search_type=SearchType.VECTOR, options=opts)
except VectorStoreOperationException as e:
    logger.warning("filter rejected: %s", e.__cause__ or e)
    results = None

Prevention

When it happens

Trigger: Using a ternary (`a if c else b`), a comprehension (`[i for i in ...]`), an f-string, starred arguments, or a walrus assignment inside a filter lambda.

Common situations: Writing Pythonic one-liners that use comprehensions or ternaries; copy-pasting general expressions; expecting full Python in the sandbox.

Related errors


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