dotnet/efcore · error · InvalidOperationException
'HasShadowId' was called on a non-root entity type
Error message
'HasShadowId' was called on a non-root entity type '{entityType}'. JSON 'id' configuration can only be made on the document root. What it means
Thrown when HasShadowId is configured on a non-root entity type. The shadow-id flag tells EF to manage a shadow 'id' property, which is a document-root concern because the 'id' field belongs to the whole document. The validator detects the HasShadowId annotation on any type where IsDocumentRoot() is false and throws.
Solutions
- Move the HasShadowId call to the document-root entity type.
- If using IEntityTypeConfiguration<T>, only invoke it on the root.
- Remove the call from derived types; the root manages the shadow id for the hierarchy.
Example fix
// before modelBuilder.Entity<DerivedOrder>().HasShadowId(); // after modelBuilder.Entity<Order>().HasShadowId();
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>().HasShadowId(); Prevention
- Call HasShadowId only on the document root.
- Keep shadow-id configuration in a root-only IEntityTypeConfiguration.
- Add a unit test asserting no derived type carries the HasShadowId annotation.
When it happens
Trigger: Calling HasShadowId() on a derived entity (one with a BaseType). Fires during ValidateDiscriminatorMappings.
Common situations: Applying HasShadowId inside a per-derived-type configuration; copying a root configuration onto a derived type; misunderstanding that shadow id is a per-document (root) concern.
Related errors
- 'HasDiscriminatorInJsonId' or…
- A partition key is defined on entity type
- Cosmos automatic-indexing configuration was set on entity…
- A full-text index is defined for
- A full-text index on
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/a5776418f61db69c.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs:565
/// 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)
{
base.ValidateIndex(index, logger);
if (index.GetVectorIndexType() != null)
{
ValidateVectorIndex(index, logger);View on GitHub (pinned to 3a2006ef56)