microsoft/semantic-kernel · error · NotImplementedError

Unsupported attribute chain root: {type(current)}

Error message

Unsupported attribute chain root: {type(current)}

What it means

Raised inside _lambda_parser._parse_attribute_chain when the root of an attribute chain is not an ast.Name node. The parser walks a chain like x.a.b by following .value until it reaches the root; it expects that root to be an ast.Name (the lambda parameter, e.g. 'x'). If the chain is rooted on a call, subscript, constant, or any other expression, it cannot produce a valid OData field path and raises NotImplementedError.

Source

Thrown at python/semantic_kernel/connectors/azure_ai_search.py:647

            raise VectorSearchExecutionException("Failed to search the collection.") from exc
        return KernelSearchResults(
            results=self._get_vector_search_results_from_results(raw_results, options),
            total_count=await raw_results.get_count() if options.include_total_count else None,
        )

    @override
    def _lambda_parser(self, node: ast.AST) -> Any:
        def _parse_attribute_chain(attr_node: ast.Attribute) -> str:
            parts = []
            current = attr_node
            while isinstance(current, ast.Attribute):
                parts.append(current.attr)
                current = current.value  # type: ignore
            if isinstance(current, ast.Name):
                # skip the root variable name (e.g., 'x')
                pass
            else:
                raise NotImplementedError(f"Unsupported attribute chain root: {type(current)}")
            # reverse to get the correct order
            prop_path = "/".join(reversed(parts))
            # Check if the top-level property is in the data model
            top_level = parts[-1] if parts else None
            if top_level and top_level not in self.definition.storage_names:
                raise VectorStoreOperationException(
                    f"Field '{top_level}' not in data model (storage property names are used)."
                )
            return prop_path

        match node:
            case ast.Compare():
                if len(node.ops) > 1:
                    values: list[ast.expr] = []
                    for idx in range(len(node.ops)):
                        if idx == 0:
                            values.append(
                                ast.Compare(

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Make sure every attribute chain in the filter lambda starts at the lambda parameter (e.g. lambda x: x.field == value).
  2. Avoid calling functions or subscripting at the root of a field reference in the filter.
  3. If you need a constant from outer scope, bind it as the comparator (right-hand side), not as part of the field path.

Example fix

// before
options.filter = lambda x: helper.x.field == 1   # root is 'helper', not a Name

// after
options.filter = lambda x: x.field == 1
Defensive patterns

Strategy: validation

Validate before calling

import ast

def filter_lambdas_root_in_name(func):
    tree = ast.parse(ast.unparse(ast.fix_missing_locations(
        ast.parse("lambda x: None", mode="eval").body.replace(body=...)
    ))) if False else None
    # simpler: static lint that attribute chains in the lambda start at the lambda param
    src = ast.unparse(ast.parse(ast.getsource(func.__code__), mode="exec"))
    return src  # review manually; chains must start at the param name

# Enforce convention: all filter lambdas start chains at the lambda parameter

Try / catch

from semantic_kernel.exceptions import VectorStoreException
try:
    res = await collection.search(values=q, options=opts_with_filter)
except NotImplementedError as e:
    if "Unsupported attribute chain root" in str(e):
        opts.filter = lambda x: x.field == value  # rewrite rooted at x
        res = await collection.search(values=q, options=opts)
    raise

Prevention

When it happens

Trigger: Writing a filter lambda whose left-hand attribute access is rooted on something other than the lambda variable — e.g. lambda x: some_obj.field == 1, lambda x: get(x).field == 1, or lambda x: x.items[0].field == 1 (subscript root). The parser only supports chains beginning at the lambda parameter name.

Common situations: Referencing a module-level or outer-scope object inside a filter lambda instead of the record parameter; chaining off a method call; using subscript indexing at the base of an attribute chain.

Related errors


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