{"record":{"id":"ce8786ae285e6ed3","repo":"microsoft/semantic-kernel","slug":"field-top-level-not-in-data-model-storage-pro","errorCode":null,"errorMessage":"Field '{top_level}' not in data model (storage property names are used).","messagePattern":"Field '(.+?)' not in data model \\(storage property names are used\\)\\.","errorType":"exception","errorClass":"VectorStoreOperationException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/azure_ai_search.py","lineNumber":653,"sourceCode":"    @override\n    def _lambda_parser(self, node: ast.AST) -> Any:\n        def _parse_attribute_chain(attr_node: ast.Attribute) -> str:\n            parts = []\n            current = attr_node\n            while isinstance(current, ast.Attribute):\n                parts.append(current.attr)\n                current = current.value  # type: ignore\n            if isinstance(current, ast.Name):\n                # skip the root variable name (e.g., 'x')\n                pass\n            else:\n                raise NotImplementedError(f\"Unsupported attribute chain root: {type(current)}\")\n            # reverse to get the correct order\n            prop_path = \"/\".join(reversed(parts))\n            # Check if the top-level property is in the data model\n            top_level = parts[-1] if parts else None\n            if top_level and top_level not in self.definition.storage_names:\n                raise VectorStoreOperationException(\n                    f\"Field '{top_level}' not in data model (storage property names are used).\"\n                )\n            return prop_path\n\n        match node:\n            case ast.Compare():\n                if len(node.ops) > 1:\n                    values: list[ast.expr] = []\n                    for idx in range(len(node.ops)):\n                        if idx == 0:\n                            values.append(\n                                ast.Compare(\n                                    left=node.left,\n                                    ops=[node.ops[idx]],\n                                    comparators=[node.comparators[idx]],\n                                )\n                            )\n                        else:","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/azure_ai_search.py#L635-L671","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use only field names that exist in definition.storage_names (the serialized/storage names, not necessarily the Python attribute names).","If the field has a storage_name alias, reference that storage name in the lambda.","Add the missing field to the VectorStoreCollectionDefinition if it should be filterable."],"exampleFix":"// before\noptions.filter = lambda x: x.createdAt < 5   # Python name, but storage_name is 'created_at'\n\n// after\noptions.filter = lambda x: x.created_at < 5","handlingStrategy":"validation","validationCode":"def validate_filter_fields_against_model(filter_lambda, definition) -> None:\n    import ast\n    src = ast.getsource(filter_lambda)\n    tree = ast.parse(src.strip() if src.startswith(\"lambda\") else src, mode=\"exec\")\n    for node in ast.walk(tree):\n        if isinstance(node, ast.Attribute):\n            chain = node\n            while isinstance(chain, ast.Attribute):\n                chain = chain.value\n            # top-level attr is node.attr when chain rooted at Name\n            top = node.attr\n            if isinstance(node.value, ast.Name):\n                assert top in definition.storage_names, (\n                    f\"Filter field '{top}' not in model storage_names {set(definition.storage_names)}\"\n                )\n\nvalidate_filter_fields_against_model(opts.filter, collection.definition)","typeGuard":null,"tryCatchPattern":"from semantic_kernel.exceptions import VectorStoreOperationException\ntry:\n    res = await collection.search(values=q, options=opts)\nexcept VectorStoreOperationException as e:\n    if \"not in data model\" in str(e):\n        opts.filter = lambda x: x.created_at < 5  # use correct storage_name\n        res = await collection.search(values=q, options=opts)\n    raise","preventionTips":["Reference storage_names (serialized names) in filter lambdas, not Python attribute names when they differ.","Validate filter field names against definition.storage_names before searching.","Add the field to the definition if it should be filterable."],"tags":["filter","odata","data-model","azure-ai-search"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}