microsoft/semantic-kernel · error · VectorStoreOperationException

Field '{top_level}' not in data model (storage property name

Error message

Field '{top_level}' not in data model (storage property names are used).

What it means

Raised by _parse_attribute_chain when the top-level (outermost) property name in a filter attribute chain is not present in self.definition.storage_names. The connector translates filter lambdas to OData using the storage (serialized) property names of the data model, so a field name not in the model's storage names is rejected with a VectorStoreOperationException before querying the service.

Source

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

    @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(
                                    left=node.left,
                                    ops=[node.ops[idx]],
                                    comparators=[node.comparators[idx]],
                                )
                            )
                        else:

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Use only field names that exist in definition.storage_names (the serialized/storage names, not necessarily the Python attribute names).
  2. If the field has a storage_name alias, reference that storage name in the lambda.
  3. Add the missing field to the VectorStoreCollectionDefinition if it should be filterable.

Example fix

// before
options.filter = lambda x: x.createdAt < 5   # Python name, but storage_name is 'created_at'

// after
options.filter = lambda x: x.created_at < 5
Defensive patterns

Strategy: validation

Validate before calling

def validate_filter_fields_against_model(filter_lambda, definition) -> None:
    import ast
    src = ast.getsource(filter_lambda)
    tree = ast.parse(src.strip() if src.startswith("lambda") else src, mode="exec")
    for node in ast.walk(tree):
        if isinstance(node, ast.Attribute):
            chain = node
            while isinstance(chain, ast.Attribute):
                chain = chain.value
            # top-level attr is node.attr when chain rooted at Name
            top = node.attr
            if isinstance(node.value, ast.Name):
                assert top in definition.storage_names, (
                    f"Filter field '{top}' not in model storage_names {set(definition.storage_names)}"
                )

validate_filter_fields_against_model(opts.filter, collection.definition)

Try / catch

from semantic_kernel.exceptions import VectorStoreOperationException
try:
    res = await collection.search(values=q, options=opts)
except VectorStoreOperationException as e:
    if "not in data model" in str(e):
        opts.filter = lambda x: x.created_at < 5  # use correct storage_name
        res = await collection.search(values=q, options=opts)
    raise

Prevention

When it happens

Trigger: Writing a filter lambda like lambda x: x.nonexistent_field == 1, or using the Python property name when the field has a different storage_name (the serialized name). The check is against storage_names, not the Python attribute names.

Common situations: Typos in field names; using the Python attribute name instead of the storage_name when they differ; referencing a field that was removed from the model; filtering on a field that exists only in the index but not in the VectorStoreCollectionDefinition.

Related errors


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