dotnet/efcore · error · InvalidOperationException
'HasDiscriminatorInJsonId' or…
Error message
'HasDiscriminatorInJsonId' or 'HasRootDiscriminatorInJsonId' was called on a non-root entity type '{entityType}'. Discriminator configuration for the JSON 'id' property can only be made on the document root. What it means
Thrown when HasDiscriminatorInJsonId or HasRootDiscriminatorInJsonId is configured on a non-root entity type. Discriminator-in-JSON-id configuration controls how the discriminator value is encoded into the document 'id' field, which is a document-root concern. The validator checks for the DiscriminatorInKey annotation on any entity whose IsDocumentRoot() is false and throws.
Solutions
- Move the HasDiscriminatorInJsonId / HasRootDiscriminatorInJsonId call to the document-root entity type of the hierarchy.
- If using IEntityTypeConfiguration<T>, only register this configuration for the root type.
- Remove the call from derived types entirely; the root setting covers the whole hierarchy.
Example fix
// before
modelBuilder.Entity<DerivedOrder>() // : Order
.HasDiscriminatorInJsonId();
// after
modelBuilder.Entity<Order>() // the document root
.HasDiscriminatorInJsonId(); Defensive patterns
Strategy: type-guard
Type guard
static bool IsDocumentRoot(Type t, IModel model)
=> model.FindEntityType(t)?.IsDocumentRoot() ?? false;
if (IsDocumentRoot(typeof(Order), modelBuilder.Model))
modelBuilder.Entity<Order>().HasDiscriminatorInJsonId(); Prevention
- Call HasDiscriminatorInJsonId / HasRootDiscriminatorInJsonId only on the document root.
- Use IEntityTypeConfiguration<TRoot> for id-discriminator configuration.
- Add a CI test asserting no non-root type carries the DiscriminatorInKey annotation.
When it happens
Trigger: Calling HasDiscriminatorInJsonId() (or HasRootDiscriminatorInJsonId()) on an entity type that is part of a hierarchy but is not the document root. Fires during ValidateDiscriminatorMappings at model finalization.
Common situations: Applying id-discriminator configuration inside a per-derived-type config; copying a root configuration to derived types; not realizing the call is meaningful only on the root.
Related errors
- 'HasShadowId' was called on a non-root entity type
- A partition key is defined on entity type
- Cosmos automatic-indexing configuration was set on entity…
- The IsDiscriminatorMappingComplete setting was configured to
- A full-text index is defined for
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/7a8b9af505bfd15f.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs:559
properties[jsonName] = navigation;
}
}
/// <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>
protected virtual void ValidateDiscriminatorMappings(
IEntityType entityType,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
if (!entityType.IsDocumentRoot()
&& entityType.FindAnnotation(CosmosAnnotationNames.DiscriminatorInKey) != null)
{
throw new InvalidOperationException(CosmosStrings.DiscriminatorInKeyOnNonRoot(entityType.DisplayName()));
}
if (!entityType.IsDocumentRoot()
&& entityType.FindAnnotation(CosmosAnnotationNames.HasShadowId) != null)
{
throw new InvalidOperationException(CosmosStrings.HasShadowIdOnNonRoot(entityType.DisplayName()));
}
}
/// <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>
protected override void ValidateIndex(
IIndex index,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)View on GitHub (pinned to 3a2006ef56)