dotnet/efcore · error · InvalidOperationException

A partition key is defined on entity type '{entityType}', wh

Error message

A partition key is defined on entity type '{entityType}', which inherits from '{baseEntityType}'. Partition keys must be defined on the root entity type of a hierarchy.

What it means

Thrown by ValidateKeys when an entity type that has a BaseType (a derived type in an inheritance hierarchy) directly carries the PartitionKeyNames annotation. Partition keys are container-scoped and must be declared on the root entity type of the hierarchy; derived types inherit the partition key. The check uses FindAnnotation to detect a locally-set (not inherited) partition key.

Source

Thrown at src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs:458

        var idType = idProperty.GetTypeMapping().Converter?.ProviderClrType
            ?? idProperty.ClrType;
        if (idType != typeof(string))
        {
            throw new InvalidOperationException(
                CosmosStrings.IdNonStringStoreType(idProperty.Name, entityType.DisplayName(), idType.ShortDisplayName()));
        }

        var partitionKeyPropertyNames = entityType.GetPartitionKeyPropertyNames();
        if (partitionKeyPropertyNames.Count == 0)
        {
            logger.NoPartitionKeyDefined(entityType);
        }
        else
        {
            if (entityType.BaseType != null
                && entityType.FindAnnotation(CosmosAnnotationNames.PartitionKeyNames)?.Value != null)
            {
                throw new InvalidOperationException(
                    CosmosStrings.PartitionKeyNotOnRoot(entityType.DisplayName(), entityType.BaseType.DisplayName()));
            }

            foreach (var partitionKeyPropertyName in partitionKeyPropertyNames)
            {
                var partitionKey = entityType.FindProperty(partitionKeyPropertyName);
                if (partitionKey == null)
                {
                    throw new InvalidOperationException(
                        CosmosStrings.PartitionKeyMissingProperty(entityType.DisplayName(), partitionKeyPropertyName));
                }

                var partitionKeyType = (partitionKey.GetTypeMapping().Converter?.ProviderClrType
                    ?? partitionKey.ClrType).UnwrapNullableType();
                if (partitionKeyType != typeof(string)
                    && !partitionKeyType.IsNumeric()
                    && partitionKeyType != typeof(bool))
                {

View on GitHub (pinned to dbf9771522)

Solutions

  1. Move the HasPartitionKey call to the root entity type of the hierarchy.
  2. Guard shared config helpers with `if (entityType.BaseType == null)` before setting partition keys.
  3. Remove the partition-key annotation from the derived type.

Example fix

// before
modelBuilder.Entity<Manager>().HasPartitionKey(m => m.DepartmentId); // Manager derives from Employee

// after
modelBuilder.Entity<Employee>().HasPartitionKey(e => e.DepartmentId);
Defensive patterns

Strategy: validation

Validate before calling

using var ctx = new MyContext();
foreach (var e in ctx.Model.GetEntityTypes().Where(t => t.BaseType is not null))
{
    Debug.Assert(e.FindAnnotation("Cosmos:PartitionKeyNames") is null,
        $"{e.Name} (derived) must not declare a partition key; set it on the root.");
}

Type guard

static bool IsHierarchyRoot(Microsoft.EntityFrameworkCore.Metadata.IEntityType e) => e.BaseType is null;

Try / catch

try { ctx.Model.GetModel(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Partition keys must be defined on the root"))
{
    logger.LogError(ex, "HasPartitionKey was called on a derived entity type");
    throw;
}

Prevention

When it happens

Trigger: Calling HasPartitionKey(...) on an EntityTypeBuilder whose entity derives from a base entity (TPH hierarchy sharing a container).

Common situations: Applying a generic 'configure partition key' helper to every entity in a hierarchy; splitting configuration across partial OnModelCreating methods where the derived config runs.

Related errors


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