dotnet/efcore · error · InvalidOperationException

The index over properties

Error message

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.

What it means

Thrown when a regular (non-vector, non-full-text) index is declared on an owned entity type that is not a document root. Cosmos only allows indexes that traverse owned types if they are vector indexes or full-text indexes; regular property indexes must be declared on the document root. The validator walks the owned-navigation tree via EnumerateContainerIndexes and rejects any plain index whose declaring type is not a document root.

Solutions

  1. Move the HasIndex call to the document-root entity. If you need to query by a nested property, expose it on the root or restructure so the property lives on the root.
  2. If the index is actually for vector search, configure it as a vector index (HasVectorIndex) so the validator allows it on the owned path.
  3. If the index is for full-text search, mark it as a full-text index so it is exempted.
  4. Drop the index; Cosmos automatic indexing already covers individual nested paths by default.

Example fix

// before
modelBuilder.Entity<Order>().OwnsOne(o => o.Shipping, sb =>
{
    sb.HasIndex(s => s.ZipCode); // regular index on owned type
});

// after - either move to root or rely on automatic indexing
modelBuilder.Entity<Order>().HasIndex(o => o.Shipping.ZipCode); // not valid either; instead, query via automatic indexing or denormalize
// Or simply remove the explicit index - Cosmos auto-indexes nested properties.
Defensive patterns

Strategy: validation

Validate before calling

foreach (var et in dbContext.Model.GetEntityTypes())
{
    foreach (var idx in et.GetIndexes())
    {
        if (!et.IsDocumentRoot()
            && idx.GetVectorIndexType() is null
            && idx.IsFullTextIndex() != true)
        {
            throw new InvalidOperationException($"Index on owned type {et.DisplayName()}");
        }
    }
}

Prevention

When it happens

Trigger: Calling HasIndex on an owned entity configuration (OwnedTypeBuilder) for properties of the owned type. The error fires because the declaring entity type is not a document root and the index is neither a vector index nor a full-text index.

Common situations: Modeling an owned complex type and wanting to index one of its scalar properties; porting a relational model that used owned-type indexes verbatim to Cosmos; assuming Cosmos indexes nested JSON the same way as relational indexes owned tables.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/b76d4114bd841baa. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs:389

                        throw new InvalidOperationException(
                            CosmosStrings.InconsistentAutomaticIndexing(
                                container,
                                automaticIndexingOwner.DisplayName(),
                                entityType.DisplayName()));
                    }
                }
            }

            // Walk the full owned/complex tree to surface every HasIndex declared in this container.
            // Vector and full-text indexes are allowed to traverse owned types; only regular indexes are
            // rejected
            foreach (var (declaringEntityType, index) in EnumerateContainerIndexes(entityType))
            {
                if (!declaringEntityType.IsDocumentRoot()
                    && index.GetVectorIndexType() == null
                    && index.IsFullTextIndex() != true)
                {
                    throw new InvalidOperationException(
                        CosmosStrings.IndexOnOwnedType(
                            string.Join(",", index.Properties.Select(e => e.Name)),
                            declaringEntityType.DisplayName(),
                            entityType.DisplayName()));
                }
            }
        }
    }

    private static IEnumerable<(IEntityType DeclaringEntityType, IIndex Index)> EnumerateContainerIndexes(IEntityType root)
    {
        foreach (var index in root.GetIndexes())
        {
            yield return (root, index);
        }

        foreach (var ownedNav in root.GetNavigations()
                     .Where(n => n.ForeignKey.IsOwnership && !n.IsOnDependent && !n.TargetEntityType.IsDocumentRoot()))

View on GitHub (pinned to 3a2006ef56)