{"record":{"id":"bfa9571fef3e52ba","repo":"dotnet/efcore","slug":"the-specified-discriminator-value-value-for","errorCode":null,"errorMessage":"The specified discriminator value '{value}' for '{entityType}' is not a string. Configure a string discriminator value instead.","messagePattern":"The specified discriminator value '(.+?)' for '(.+?)' is not a string\\. Configure a string discriminator value instead\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":2151,"sourceCode":"            var derivedTypes = entityType.GetDerivedTypesInclusive().ToList();\n            var discriminatorValues = new Dictionary<string, IEntityType>();\n            foreach (var derivedType in derivedTypes)\n            {\n                foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())\n                {\n                    ValidateDiscriminatorValues(complexProperty.ComplexType);\n                }\n\n                var discriminatorValue = derivedType.GetDiscriminatorValue();\n                if (!derivedType.ClrType.IsInstantiable()\n                    || discriminatorValue is null)\n                {\n                    continue;\n                }\n\n                if (discriminatorValue is not string valueString)\n                {\n                    throw new InvalidOperationException(\n                        RelationalStrings.NonTphDiscriminatorValueNotString(discriminatorValue, derivedType.DisplayName()));\n                }\n\n                if (discriminatorValues.TryGetValue(valueString, out var duplicateEntityType))\n                {\n                    throw new InvalidOperationException(\n                        RelationalStrings.EntityShortNameNotUnique(\n                            derivedType.Name, discriminatorValue, duplicateEntityType.Name));\n                }\n\n                discriminatorValues[valueString] = derivedType;\n            }\n        }\n    }\n\n    /// <summary>\n    ///     Validates the key value generation is valid.\n    /// </summary>","sourceCodeStart":2133,"sourceCodeEnd":2169,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L2133-L2169","documentation":"Thrown inside ValidateDiscriminatorValues (the non-TPH branch) when a concrete derived type's discriminator value, returned by GetDiscriminatorValue(), cannot be cast to string. In TPT/TPC hierarchies the discriminator value defaults to the entity's short name (a string), so any explicitly-set non-string discriminator value is rejected.","triggerScenarios":"In a TPT or TPC hierarchy, calling SetDiscriminatorValue(42) or SetDiscriminatorValue(true) (any non-string), or applying a [Discriminator] with an int/enum/bool value on a derived type. Only abstract/non-instantiable types are skipped; concrete types are checked.","commonSituations":"Porting a TPH model that used integer discriminator values into a TPT/TPC hierarchy without changing them; assuming discriminator values are type-flexible across all strategies.","solutions":["Set a string discriminator value instead: SetDiscriminatorValue(\"Customer\").","Remove the custom discriminator value and let EF use the entity short name automatically.","If you need numeric discriminator semantics, stay on TPH mapping where non-string discriminator values are supported."],"exampleFix":"// before (TPT hierarchy)\nmodelBuilder.Entity<Customer>().HasBaseType<Person>()\n    .Property(\"Discriminator\").HasConversion<string>();\nmodelBuilder.Entity<Customer>().Metadata.SetDiscriminatorValue(2); // int -> throws 422\n\n// after\nmodelBuilder.Entity<Customer>().Metadata.SetDiscriminatorValue(\"Customer\");","handlingStrategy":"validation","validationCode":"foreach (var entityType in modelBuilder.Model.GetEntityTypes()\n    .Where(t => t.BaseType != null))\n{\n    var root = entityType.GetRootType();\n    var strategy = root.GetMappingStrategy();\n    if (strategy != RelationalAnnotationNames.TphMappingStrategy\n        && strategy != null)\n    {\n        var dv = entityType.GetDiscriminatorValue();\n        if (entityType.ClrType.IsInstantiable() && dv is not null and not string)\n        {\n            throw new InvalidOperationException($\"{entityType.DisplayName()} has non-string discriminator value {dv}; set a string.\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In TPT/TPC hierarchies, only ever set string discriminator values (or none at all).","Keep integer/enum discriminator values for TPH-only models.","Add a test that finalizes the model so this surfaces during CI rather than at runtime."],"tags":["ef-core","model-validation","mapping","inheritance","tpt","tpc","discriminator"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}