{"record":{"id":"b76d4114bd841baa","repo":"dotnet/efcore","slug":"the-index-over-properties-properties-is-declar","errorCode":null,"errorMessage":"The index over properties '{properties}' is declared on owned type '{ownedEntityType}', which is mapped to container '{containerEntityType}'. Indexes that traverse owned types are not currently supported.","messagePattern":"The index over properties '(.+?)' is declared on owned type '(.+?)', which is mapped to container '(.+?)'\\. Indexes that traverse owned types are not currently supported\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs","lineNumber":389,"sourceCode":"                        throw new InvalidOperationException(\n                            CosmosStrings.InconsistentAutomaticIndexing(\n                                container,\n                                automaticIndexingOwner.DisplayName(),\n                                entityType.DisplayName()));\n                    }\n                }\n            }\n\n            // Walk the full owned/complex tree to surface every HasIndex declared in this container.\n            // Vector and full-text indexes are allowed to traverse owned types; only regular indexes are\n            // rejected\n            foreach (var (declaringEntityType, index) in EnumerateContainerIndexes(entityType))\n            {\n                if (!declaringEntityType.IsDocumentRoot()\n                    && index.GetVectorIndexType() == null\n                    && index.IsFullTextIndex() != true)\n                {\n                    throw new InvalidOperationException(\n                        CosmosStrings.IndexOnOwnedType(\n                            string.Join(\",\", index.Properties.Select(e => e.Name)),\n                            declaringEntityType.DisplayName(),\n                            entityType.DisplayName()));\n                }\n            }\n        }\n    }\n\n    private static IEnumerable<(IEntityType DeclaringEntityType, IIndex Index)> EnumerateContainerIndexes(IEntityType root)\n    {\n        foreach (var index in root.GetIndexes())\n        {\n            yield return (root, index);\n        }\n\n        foreach (var ownedNav in root.GetNavigations()\n                     .Where(n => n.ForeignKey.IsOwnership && !n.IsOnDependent && !n.TargetEntityType.IsDocumentRoot()))","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs#L371-L407","documentation":"Thrown by ValidateContainerIndexing while walking the owned-type tree (EnumerateContainerIndexes) when a regular (non-vector, non-full-text) index is declared on an owned entity type. The validator walks ownership navigations from each document root; any HasIndex declared on a non-document-root type that is not a vector or full-text index is rejected, because Cosmos indexes cannot traverse embedded owned documents in the relational sense.","triggerScenarios":"Calling modelBuilder.Entity<Owner>().OwnsOne(o => o.Address, a => a.HasIndex(x => x.Zip)) where Address is an owned type embedded in the Owner document. Regular indexes on owned types are unsupported; only vector and full-text indexes may cross the ownership boundary.","commonSituations":"Porting a relational model that indexed properties now nested inside owned types; wanting to 'speed up' lookups on a nested value; treating owned types like separate tables.","solutions":["Remove the HasIndex from the owned type.","If you need to query by the nested value, declare the index on the document-root entity over the flattened path, or restructure the property onto the root.","If the index is genuinely a vector or full-text index, use the vector/full-text APIs which are allowed to traverse owned types."],"exampleFix":"// before\nmodelBuilder.Entity<Customer>().OwnsOne(c => c.Address, a =>\n{\n    a.HasIndex(x => x.PostalCode); // Address is owned -> rejected\n});\n\n// after\nmodelBuilder.Entity<Customer>().OwnsOne(c => c.Address);\n// query by the nested JSON path directly; no HasIndex on the owned type","handlingStrategy":"validation","validationCode":"using var ctx = new MyContext();\nforeach (var root in ctx.Model.GetEntityTypes().Where(e => e.FindPrimaryKey() != null))\n{\n    foreach (var nav in root.GetNavigations().Where(n => n.ForeignKey.IsOwnership && !n.IsOnDependent))\n    {\n        foreach (var idx in nav.TargetEntityType.GetIndexes())\n        {\n            var isVector = idx.GetVectorIndexType() is not null;\n            var isFullText = idx.IsFullTextIndex() == true;\n            Debug.Assert(isVector || isFullText,\n                $\"Index [{string.Join(\",\", idx.Properties.Select(p => p.Name))}] on owned {nav.TargetEntityType.Name} is not allowed (only vector/full-text may traverse owned types).\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try { ctx.Model.GetModel(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Indexes that traverse owned types are not currently supported\"))\n{\n    logger.LogError(ex, \"A regular HasIndex was declared on an owned type\");\n    throw;\n}","preventionTips":["Do not declare regular HasIndex on owned entity types; declare indexes on the document root.","Use vector (.IsVectorIndex) or full-text (.IsFullTextSearch) indexes if you must index across owned types.","When porting relational models, audit every HasIndex inside OwnsOne/OwnsMany blocks."],"tags":["cosmos","configuration","model-validation","indexing","owned-types"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}