microsoft/semantic-kernel · error · VectorStoreOperationException

Call target node type '{type(node.func).__name__}' is not al

Error message

Call target node type '{type(node.func).__name__}' is not allowed in filter expressions.

What it means

Thrown by _eval_Call (in_memory.py:307-309) when a Call node's func is neither an ast.Name nor an ast.Attribute (e.g. calling the result of a subscript or another call). This is defense-in-depth: the parse-time Call validation (in_memory.py:796-806) rejects such constructs earlier with a different message, so this branch is only reachable if the allowlist has been subclassed/relaxed to admit chained or subscript calls without teaching the evaluator to handle them.

Source

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

            if node.func.attr == "contains":
                if len(args) != 1:
                    raise VectorStoreOperationException("Method 'contains' expects exactly one argument.")
                return args[0] in target

            try:
                func = getattr(target, node.func.attr)
            except AttributeError as e:
                raise VectorStoreOperationException(
                    f"Method '{node.func.attr}' is not available in filter expressions."
                ) from e

            if not callable(func):
                raise VectorStoreOperationException(
                    f"Attribute '{node.func.attr}' is not callable in filter expressions."
                )
            return func(*args)

        raise VectorStoreOperationException(
            f"Call target node type '{type(node.func).__name__}' is not allowed in filter expressions."
        )

    def _compare(self, operator_node: ast.AST, left: Any, right: Any) -> bool:
        """Evaluate a comparison operator."""
        if isinstance(operator_node, ast.Eq):
            return left == right
        if isinstance(operator_node, ast.NotEq):
            return left != right
        if isinstance(operator_node, ast.Lt):
            return left < right
        if isinstance(operator_node, ast.LtE):
            return left <= right
        if isinstance(operator_node, ast.Gt):
            return left > right
        if isinstance(operator_node, ast.GtE):
            return left >= right
        if isinstance(operator_node, ast.In):

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Do not relax allowed_filter_ast_nodes to permit chained/subscript/lambda call targets.
  2. If you genuinely need such calls, override _eval_Call in your subclass to handle the extra target types safely.
  3. Keep the parse-time Call validation intact so unsupported call shapes are rejected before evaluation.
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: Subclassing InMemoryCollection and adding ast.Subscript, ast.Call, or ast.Lambda to allowed_filter_ast_nodes so a filter like `lambda x: x[0]()` or `lambda x: (lambda y: y)(x)` passes parsing, then reaches an evaluator that cannot dispatch the call target.

Common situations: Extending the filter sandbox via subclassing without extending _eval_Call; importing filters from an untrusted source that the subclass is more permissive about.

Related errors


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