dotnet/efcore · error · InvalidOperationException

The specified discriminator value '{value}' for '{entityType

Error message

The specified discriminator value '{value}' for '{entityType}' is not a string. Configure a string discriminator value instead.

What it means

In a non-TPH hierarchy (TPT or TPC), EF Core uses the entity 'short name' as an implicit discriminator value, and these implicit discriminator values must be strings. A configured discriminator value that is not a string was found on a derived type. Thrown from ValidateInheritanceMapping while iterating derived types in a non-TPH hierarchy.

Source

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

            var derivedTypes = entityType.GetDerivedTypesInclusive().ToList();
            var discriminatorValues = new Dictionary<string, IEntityType>();
            foreach (var derivedType in derivedTypes)
            {
                foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())
                {
                    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>

View on GitHub (pinned to dbf9771522)

Solutions

  1. Change HasValue(...) to supply a string discriminator value (e.g. HasValue("Student")).
  2. Remove the custom discriminator value and let EF use the entity short name (a string by default).
  3. If a non-string discriminator is required, use TPH instead of TPT/TPC.

Example fix

// before (TPT hierarchy)
modelBuilder.Entity<Person>()
    .UseTptMappingStrategy()
    .HasDiscriminator(p => p.Kind)
    .HasValue<Student>(1)
    .HasValue<Teacher>(2);
// after (string discriminator values in TPT/TPC)
modelBuilder.Entity<Person>()
    .UseTptMappingStrategy()
    .HasDiscriminator(p => p.Kind)
    .HasValue<Student>("Student")
    .HasValue<Teacher>("Teacher");
Defensive patterns

Strategy: validation

Validate before calling

using (var ctx = new MyContext()) { ctx.Model.ToDebugString(); }
// Assert all discriminator values in non-TPH hierarchies are strings.
foreach (var root in ctx.Model.GetEntityTypes().Where(e => e.BaseType == null))
{
    var s = (string?)root[Microsoft.EntityFrameworkCore.Metadata.RelationalAnnotationNames.MappingStrategy];
    if (s is null or "TPH") continue;
    foreach (var d in root.GetDerivedTypesInclusive())
    {
        var v = d.GetDiscriminatorValue();
        if (v is not null and not string)
            Debug.Fail($"Discriminator value {v} ({v!.GetType().Name}) on {d.Name} is not a string under {s}.");
    }
}

Try / catch

try { using var ctx = new MyContext(); _ = ctx.Model; }
catch (InvalidOperationException ex) when (ex.Message.Contains("is not a string. Configure a string discriminator value"))
{ log.Error("Non-string discriminator in TPT/TPC; use a string value or switch to TPH: {Msg}", ex.Message); throw; }

Prevention

When it happens

Trigger: Calling HasDiscriminator(...).HasValue(42) (int) or .HasValue(MyEnum.A) on a derived entity in a hierarchy configured with UseTptMappingStrategy() or UseTpcMappingStrategy(); using an enum or numeric discriminator value in TPT/TPC.

Common situations: Carrying over an int/enum discriminator configuration when migrating from TPH to TPT/TPC; using value-typed discriminators by habit in a TPC hierarchy; mixing TPH-style discriminator values into a non-TPH hierarchy.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/bfa9571fef3e52ba. Report an issue: GitHub.