{"record":{"id":"199a679885e330cf","repo":"dotnet/efcore","slug":"the-indexes-index1-on-entitytype1-and-index","errorCode":null,"errorMessage":"The indexes {index1} on '{entityType1}' and {index2} on '{entityType2}' are both mapped to '{indexName}', but are declared on different tables ('{table1}' and '{table2}').","messagePattern":"The indexes (.+?) on '(.+?)' and (.+?) on '(.+?)' are both mapped to '(.+?)', but are declared on different tables \\('(.+?)' and '(.+?)'\\)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs","lineNumber":55,"sourceCode":"    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public static bool AreCompatible(\n        this IReadOnlyIndex index,\n        IReadOnlyIndex duplicateIndex,\n        in StoreObjectIdentifier storeObject,\n        bool shouldThrow)\n    {\n        var columnNames = index.GetColumnNames(storeObject);\n        var duplicateColumnNames = duplicateIndex.GetColumnNames(storeObject);\n        if (columnNames == null\n            || duplicateColumnNames == null)\n        {\n            return shouldThrow\n                ? throw new InvalidOperationException(\n                    RelationalStrings.DuplicateIndexTableMismatch(\n                        index.DisplayName(),\n                        index.DeclaringEntityType.DisplayName(),\n                        duplicateIndex.DisplayName(),\n                        duplicateIndex.DeclaringEntityType.DisplayName(),\n                        index.GetDatabaseName(storeObject),\n                        index.DeclaringEntityType.GetSchemaQualifiedTableName(),\n                        duplicateIndex.DeclaringEntityType.GetSchemaQualifiedTableName()))\n                : false;\n        }\n\n        if (!columnNames.SequenceEqual(duplicateColumnNames))\n        {\n            return shouldThrow\n                ? throw new InvalidOperationException(\n                    RelationalStrings.DuplicateIndexColumnMismatch(\n                        index.DisplayName(),\n                        index.DeclaringEntityType.DisplayName(),","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs#L37-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Give each index a distinct database name via HasDatabaseName(...).","Remove one of the duplicate index definitions if only one is needed.","Ensure both indexes are mapped to the same table if the shared name is intentional.","When migrating between mapping strategies (TPH<->TPT), regenerate the index names."],"exampleFix":"// before: TPT, both default to 'IX_Base_Name' across two tables\nmodelBuilder.Entity<Base>().HasIndex(b => b.Name);\nmodelBuilder.Entity<Derived>().ToTable(\"Derived\").HasIndex(d => d.Name);\n\n// after\nmodelBuilder.Entity<Base>().HasIndex(b => b.Name).HasDatabaseName(\"IX_Base_Name\");\nmodelBuilder.Entity<Derived>().ToTable(\"Derived\").HasIndex(d => d.Name).HasDatabaseName(\"IX_Derived_Name\");","handlingStrategy":"validation","validationCode":"// Find indexes whose database names collide across different tables.\nvar indexes = modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetDeclaredIndexes());\nforeach (var idx in indexes)\n{\n    var name = idx.GetDatabaseName();\n    foreach (var dup in indexes.Where(o => o != idx && o.GetDatabaseName() == name\n                                          && o.DeclaringEntityType.GetSchemaQualifiedTableName() != idx.DeclaringEntityType.GetSchemaQualifiedTableName()))\n    {\n        Console.WriteLine($\"Index name '{name}' used on two tables: {idx.DeclaringEntityType.Name} and {dup.DeclaringEntityType.Name}.\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set explicit HasDatabaseName for every index in TPT/Tpc hierarchies.","Review index definitions after switching inheritance mapping strategies.","Add a model validation test."],"tags":["ef-core","relational-metadata","index","model-validation","tpt"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}