dotnet/efcore · error · InvalidOperationException
The short name for ' ' is ' ' which is the same for ' '…
Error message
The short name for '{entityType1}' is '{discriminatorValue}' which is the same for '{entityType2}'. Every concrete entity type in the hierarchy must have a unique short name. Either rename one of the types or call 'modelBuilder.Entity<TEntity>().Metadata.SetDiscriminatorValue("NewShortName")'. What it means
Thrown inside ValidateDiscriminatorValues when two concrete derived types in a non-TPH hierarchy resolve to the same string discriminator value (the duplicate is found via discriminatorValues.TryGetValue(valueString, ...)). In TPT/TPC the discriminator value defaults to the entity short name (the simple type name), so two types sharing a short name collide.
Solutions
- Rename one of the colliding types so the simple names differ.
- Assign a unique discriminator value explicitly: modelBuilder.Entity<TEntity>().Metadata.SetDiscriminatorValue("NewShortName").
- Move one of the types into a different hierarchy or make it standalone.
Example fix
// before
namespace App.Admin { class User : Person {} }
namespace App.Store { class User : Person {} } // both short name "User" -> throws 423
// after
modelBuilder.Entity<App.Admin.User>().Metadata.SetDiscriminatorValue("AdminUser");
modelBuilder.Entity<App.Store.User>().Metadata.SetDiscriminatorValue("StoreUser"); Defensive patterns
Strategy: validation
Validate before calling
var byShortName = new Dictionary<string, List<string>>();
foreach (var entityType in modelBuilder.Model.GetEntityTypes()
.Where(t => t.BaseType != null && t.ClrType.IsInstantiable()))
{
var root = entityType.GetRootType();
if (root.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy) continue;
var value = entityType.GetDiscriminatorValue()?.ToString() ?? entityType.ShortName();
byShortName.TryAdd(value, new());
byShortName[value].Add(entityType.DisplayName());
}
var dup = byShortName.FirstOrDefault(kv => kv.Value.Count > 1);
if (dup.Key != null)
throw new InvalidOperationException($"Duplicate short name '{dup.Key}': {string.Join(", ", dup.Value)}"); Prevention
- Avoid reusing the same simple type name for sibling entity types in a hierarchy.
- If names must repeat across namespaces, assign explicit unique discriminator values.
- Run a naming-collision check in a model test.
When it happens
Trigger: Two concrete entity types in the same hierarchy have identical simple names (e.g. NamespaceA.User and NamespaceB.User both derive from Person), or two types are explicitly assigned the same discriminator value via SetDiscriminatorValue.
Common situations: Cross-namespace name collisions; generated/DTO types with reused names; bulk import of entities where short names repeat; forgetting that the discriminator value is the type name by default.
Related errors
- The mapping strategy
- The specified discriminator value
- Both ' ' and ' ' are mapped to the table ' '. All the…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
- Both ' ' and ' ' are mapped to the view ' '. All the entity…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/a625818103e96275.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2157
ValidateDiscriminatorValues(complexProperty.ComplexType);
}
var discriminatorValue = derivedType.GetDiscriminatorValue();
if (!derivedType.ClrType.IsInstantiable()
|| discriminatorValue is null)
{
continue;
}
if (discriminatorValue is not string valueString)
{
throw new InvalidOperationException(
RelationalStrings.NonTphDiscriminatorValueNotString(discriminatorValue, derivedType.DisplayName()));
}
if (discriminatorValues.TryGetValue(valueString, out var duplicateEntityType))
{
throw new InvalidOperationException(
RelationalStrings.EntityShortNameNotUnique(
derivedType.Name, discriminatorValue, duplicateEntityType.Name));
}
discriminatorValues[valueString] = derivedType;
}
}
}
/// <summary>
/// Validates the key value generation is valid.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="logger">The logger to use.</param>
protected virtual void ValidateValueGeneration(
IKey key,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{View on GitHub (pinned to 3a2006ef56)