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

  1. Move the HasDiscriminatorInJsonId / HasRootDiscriminatorInJsonId call to the document-root entity type of the hierarchy.
  2. If using IEntityTypeConfiguration<T>, only register this configuration for the root type.
  3. 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

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


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)