dotnet/efcore · error · InvalidOperationException

The indexes on ' ' and on ' ' are both mapped to ' ', but…

Error message

The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{indexName}', but are declared on different tables ('{table1}' and '{table2}').

What it means

Two indexes resolve to the same database name but their declaring entities are mapped to different tables, so at least one index cannot produce a column list for the store object. A single index name cannot span two physical tables.

Solutions

  1. Give each index a distinct database name via HasDatabaseName(...).
  2. Remove one of the duplicate index definitions if only one is needed.
  3. Ensure both indexes are mapped to the same table if the shared name is intentional.
  4. When migrating between mapping strategies (TPH<->TPT), regenerate the index names.

Example fix

// before: TPT, both default to 'IX_Base_Name' across two tables
modelBuilder.Entity<Base>().HasIndex(b => b.Name);
modelBuilder.Entity<Derived>().ToTable("Derived").HasIndex(d => d.Name);

// after
modelBuilder.Entity<Base>().HasIndex(b => b.Name).HasDatabaseName("IX_Base_Name");
modelBuilder.Entity<Derived>().ToTable("Derived").HasIndex(d => d.Name).HasDatabaseName("IX_Derived_Name");
Defensive patterns

Strategy: validation

Validate before calling

// Find indexes whose database names collide across different tables.
var indexes = modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetDeclaredIndexes());
foreach (var idx in indexes)
{
    var name = idx.GetDatabaseName();
    foreach (var dup in indexes.Where(o => o != idx && o.GetDatabaseName() == name
                                          && o.DeclaringEntityType.GetSchemaQualifiedTableName() != idx.DeclaringEntityType.GetSchemaQualifiedTableName()))
    {
        Console.WriteLine($"Index name '{name}' used on two tables: {idx.DeclaringEntityType.Name} and {dup.DeclaringEntityType.Name}.");
    }
}

Prevention

When it happens

Trigger: RelationalIndexExtensions.AreCompatible with shouldThrow:true when index.GetColumnNames(storeObject) or duplicateIndex.GetColumnNames(storeObject) returns null (i.e. the index is not mapped to that store object). Happens with TPT where derived types create indexes that collapse to the same default name across tables.

Common situations: Inheritance hierarchies (TPT/Tpc) where the base and derived entities both define an index that inherits a shared name; explicit HasDatabaseName reuse across unrelated entities; switching from TPH to TPT without renaming indexes.

Related errors


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

Appendix: source

Thrown at src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs:55

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public static bool AreCompatible(
        this IReadOnlyIndex index,
        IReadOnlyIndex duplicateIndex,
        in StoreObjectIdentifier storeObject,
        bool shouldThrow)
    {
        var columnNames = index.GetColumnNames(storeObject);
        var duplicateColumnNames = duplicateIndex.GetColumnNames(storeObject);
        if (columnNames == null
            || duplicateColumnNames == null)
        {
            return shouldThrow
                ? throw new InvalidOperationException(
                    RelationalStrings.DuplicateIndexTableMismatch(
                        index.DisplayName(),
                        index.DeclaringEntityType.DisplayName(),
                        duplicateIndex.DisplayName(),
                        duplicateIndex.DeclaringEntityType.DisplayName(),
                        index.GetDatabaseName(storeObject),
                        index.DeclaringEntityType.GetSchemaQualifiedTableName(),
                        duplicateIndex.DeclaringEntityType.GetSchemaQualifiedTableName()))
                : false;
        }

        if (!columnNames.SequenceEqual(duplicateColumnNames))
        {
            return shouldThrow
                ? throw new InvalidOperationException(
                    RelationalStrings.DuplicateIndexColumnMismatch(
                        index.DisplayName(),
                        index.DeclaringEntityType.DisplayName(),

View on GitHub (pinned to 3a2006ef56)