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 properties are mapped to different JSON columns ('{firstColumn}' and '{secondColumn}'). All leaves of a JSON-path index (an index whose properties traverse a complex collection) must be contained in a single JSON column.

What it means

A single JSON-path index must keep ALL leaf properties inside ONE JSON column. The validator records the first property's container column name and throws as soon as a later property's container column differs. Splitting a composite index across two JSON documents cannot be expressed as one index.

Solutions

  1. Keep every property of the index within the same JSON container column.
  2. Split the composite index into one index per JSON column so each index targets a single document.
  3. If the properties really belong together, merge them into one JSON-mapped complex type.

Example fix

// before — leaves in two different JSON columns -> [445]
modelBuilder.Entity<Record>()
    .OwnsOne(r => r.Meta, m => m.ToJson("meta"))
    .OwnsOne(r => r.Body, b => b.ToJson("body"))
    .HasIndex(r => new { r.Meta!.Tag, r.Body!.Kind });

// after — one index per JSON column
modelBuilder.Entity<Record>()
    .OwnsOne(r => r.Meta, m => m.ToJson("meta"))
    .OwnsOne(r => r.Body, b => b.ToJson("body"));
modelBuilder.Entity<Record>().OwnsOne(r => r.Meta).HasIndex(m => m.Tag);
modelBuilder.Entity<Record>().OwnsOne(r => r.Body).HasIndex(b => b.Kind);
Defensive patterns

Strategy: validation

Validate before calling

// All leaves of a JSON-path index must share one container column.
static bool IndexUsesSingleJsonContainer(IIndex index)
{
    string? first = null;
    foreach (var p in index.Properties)
    {
        string? container = p.DeclaringType is IReadOnlyComplexType ct && ct.IsMappedToJson() ? ct.GetContainerColumnName()
            : p is IReadOnlyComplexProperty cp && cp.ComplexType.IsMappedToJson() ? cp.ComplexType.GetContainerColumnName()
            : null;
        if (container is null) continue;
        if (first is null) first = container;
        else if (first != container) return false;
    }
    return true;
}

Prevention

When it happens

Trigger: Building a composite .HasIndex((e) => new { e.Doc1.Foo, e.Doc2.Bar }) where Doc1 and Doc2 are two different JSON-mapped complex properties with different container column names.

Common situations: Indexing across two JSON columns; refactoring that split one JSON document into two; copy-paste of index definitions across entities with different JSON containers.

Related errors


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

Appendix: source

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

                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(),
                        index.DeclaringEntityType.DisplayName(),
                        firstContainer,
                        container));
            }
        }
    }

    /// <inheritdoc />
    protected override void ValidateIndexProperty(
        IIndex index,
        IPropertyBase property,
        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
    {
        // A property declared inside a JSON-mapped complex type is a valid index target: when the
        // path traverses a collection the index is a JSON-path index (CollectionIndices is non-null,
        // already enforced by Index construction); otherwise it's a scalar inside a JSON document.

View on GitHub (pinned to 3a2006ef56)