dotnet/efcore · error · InvalidOperationException
The indexes {index1} on '{entityType1}' and {index2} on '{en
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
Thrown by RelationalIndexExtensions.AreCompatible during model validation when two indexes resolve to the same database name on a store object, but GetColumnNames(storeObject) returns null for at least one of them — i.e. the indexes' properties cannot be mapped to columns on that shared table. The message frames it as 'declared on different tables' because a null column-name set means the index does not actually target the store object the duplicate lives on. EF refuses to let one index name describe two indexes that do not share a concrete column set on one table.
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 dbf9771522)
Solutions
- Rename one of the indexes so each table has a distinct database name (drop the explicit HasDatabaseName, or give the second a unique name).
- If the two entity types genuinely share one table (entity splitting / table sharing), ensure both actually map to that same table and that all index properties resolve to columns on it.
- Remove the conflicting HasIndex/HasDatabaseName on one side and let EF generate a default unique name.
- Inspect index.GetDatabaseName(storeObject) and index.GetColumnNames(storeObject) for both indexes in a validation hook to confirm they collide and why the column set is null.
Example fix
// before
modelBuilder.Entity<Customer>().HasIndex(c => c.Code).HasDatabaseName("IX_Code");
modelBuilder.Entity<Vendor>().HasIndex(v => v.Code).HasDatabaseName("IX_Code"); // different tables -> throws
// after
modelBuilder.Entity<Customer>().HasIndex(c => c.Code).HasDatabaseName("IX_Code");
modelBuilder.Entity<Vendor>().HasIndex(v => v.Code).HasDatabaseName("IX_Vendor_Code"); Defensive patterns
Strategy: validation
Validate before calling
// Before finalizing the model, ensure each shared index name resolves columns on one table
foreach (var et in model.GetEntityTypes())
{
foreach (var idx in et.GetDeclaredIndexes())
{
foreach (var table in et.GetTableMappings())
{
var storeObj = table.Table is ITable t
? StoreObjectIdentifier.Table(t.Name, t.Schema)
: (StoreObjectIdentifier?)null;
if (storeObj is { } so
&& idx.GetColumnNames(so) is null
&& !string.IsNullOrEmpty(idx.GetDatabaseName(so)))
{
throw new InvalidOperationException($"Index {idx.GetDatabaseName(so)} on {et.Name} does not resolve to columns on {so.DisplayName()}");
}
}
}
} Prevention
- Avoid hardcoding HasDatabaseName across entity types that do not share a table.
- When table-splitting, centralize index declarations on the principal entity type.
- Add an integration test that builds the model and runs ValidateModel to surface duplicate-index conflicts in CI.
When it happens
Trigger: AreCompatible(index, duplicateIndex, storeObject, shouldThrow: true) at RelationalIndexExtensions.cs:51-64, invoked from model validation when two IReadOnlyIndex instances share GetDatabaseName(storeObject) but one returns null from GetColumnNames. Happens when HasIndex(...).HasDatabaseName("X") is applied on entity types that map to different physical tables, or when a JSON-mapped/scalar property inside the index has no container column name (GetColumnNames returns null at line 158/173/194).
Common situations: Two entities each call HasDatabaseName with the same literal index name without realizing EF will treat them as one shared index; entity splitting or TPT inheritance where a derived-type index name collides with a base-type index on a different table; copying an index configuration across entity types that are not table-sharing.
Related errors
- The indexes {index1} on '{entityType1}' and {index2} on '{en
- The indexes {index1} on '{entityType1}' and {index2} on '{en
- The indexes {index1} on '{entityType1}' and {index2} on '{en
- The indexes {index1} on '{entityType1}' and {index2} on '{en
- The keys {keyProperties1} on '{entityType1}' and {keyPropert
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/199a679885e330cf.
Report an issue: GitHub.