dotnet/efcore · error · InvalidOperationException

The index on the entity type ' ' cannot be configured…

Error message

The index {indexProperties} on the entity type '{entityType}' cannot be configured because its property '{property}' traverses a complex collection but is not mapped to a JSON column.

What it means

A JSON-path index is one whose property path traverses a complex collection (so index.CollectionIndices is populated). Each leaf of such an index must live inside a JSON column so the path can be expressed in the document. If a position has a non-null CollectionIndices entry but the property at that position has no JSON container column, the index identity points into a JSON path that has no JSON document, which is invalid.

Solutions

  1. Map the complex type that contains the property to JSON (.ToJsonb() / .ToJson()) so the leaf gets a container column.
  2. Remove the index, or stop traversing the complex collection in it — index scalar properties directly on the entity instead.
  3. Clear CollectionIndices on that index position if you did not intend a JSON-path index.

Example fix

// before — index traverses a complex collection not mapped to JSON -> [444]
modelBuilder.Entity<Cart>()
    .OwnsMany(c => c.Items, i =>
    {
        i.HasIndex(x => x.Sku); // Items is a complex collection, not JSON
    });

// after — map the collection to JSON before indexing into it
modelBuilder.Entity<Cart>()
    .OwnsMany(c => c.Items, i =>
    {
        i.ToJson("items");
        i.HasIndex(x => x.Sku);
    });
Defensive patterns

Strategy: validation

Validate before calling

// Before defining a JSON-path index, ensure every traversed complex collection is JSON-mapped.
static bool IndexLeavesAreJsonContained(IIndex index)
    => index.Properties.All(p =>
        (p.DeclaringType is IReadOnlyComplexType ct && ct.IsMappedToJson())
        || (p is IReadOnlyComplexProperty cp && cp.ComplexType.IsMappedToJson()));

Type guard

static bool IsJsonPathIndexValid(IIndex index)
    => index.CollectionIndices is null
       || index.Properties.All(p =>
            (p.DeclaringType is IReadOnlyComplexType ct && ct.IsMappedToJson())
            || (p is IReadOnlyComplexProperty cp && cp.ComplexType.IsMappedToJson()));

Prevention

When it happens

Trigger: Defining a composite index whose properties include one reached through a complex collection that is NOT mapped to JSON (e.g. complex collection mapped to separate tables/columns), while CollectionIndices is set for that position. Mixing JSON-path index semantics with a non-JSON complex collection.

Common situations: Upgrading to an EF version that introduced JSON-path indexes and a previously-tolerated index now traverses a non-JSON collection; configuring .HasIndex over a path into a complex collection mapped via ToTable splitting; copying JSON index config onto a non-JSON entity.

Related errors


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

Appendix: source

Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2720

        {
            var property = index.Properties[i];

            // Only properties mapped inside a JSON container matter here. Mixed JSON / non-JSON indexes
            // are rejected earlier by ValidateIndexPropertyMapping.
            var container = property.DeclaringType is IReadOnlyComplexType complexType && complexType.IsMappedToJson()
                ? complexType.GetContainerColumnName()
                : property is IReadOnlyComplexProperty complexProperty && complexProperty.ComplexType.IsMappedToJson()
                    ? complexProperty.ComplexType.GetContainerColumnName()
                    : null;

            if (container is null)
            {
                // Not a JSON-contained property. If this position carries a non-null collection-indices
                // entry (i.e., the path traverses a complex collection but doesn't end in JSON), the
                // index identity points at a JSON path that has no JSON container — that's invalid.
                if (index.CollectionIndices?[i] is not null)
                {
                    throw new InvalidOperationException(
                        RelationalStrings.JsonPathIndexPropertyMissingJsonColumn(
                            index.Properties.Format(),
                            index.DeclaringEntityType.DisplayName(),
                            property.Name));
                }

                continue;
            }

            if (firstContainer is null)
            {
                firstContainer = container;
            }
            else if (!string.Equals(firstContainer, container, StringComparison.Ordinal))
            {
                throw new InvalidOperationException(
                    RelationalStrings.JsonPathIndexPropertiesInDifferentJsonColumns(
                        index.Properties.Format(),

View on GitHub (pinned to 3a2006ef56)