dotnet/efcore · error · InvalidOperationException

A partition key is defined on entity type

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 when a partition key is configured on a derived entity type (one with a BaseType) rather than on the root of the hierarchy. Cosmos partition keys are a container-level concern and must be defined on the document-root entity type. The validator detects a non-null PartitionKeyNames annotation on a type whose BaseType is not null.

Solutions

  1. Move the HasPartitionKey call from the derived entity to the document-root entity type of the hierarchy.
  2. If you use IEntityTypeConfiguration<T>, only register the partition-key configuration for the root type TRoot.
  3. Remove any HasPartitionKey call on derived types and rely on the root configuration.

Example fix

// before
modelBuilder.Entity<DerivedOrder>()   // : Order
    .HasPartitionKey(o => o.TenantId);

// after
modelBuilder.Entity<Order>()           // the root
    .HasPartitionKey(o => o.TenantId);
Defensive patterns

Strategy: type-guard

Type guard

// Guard: only configure partition key when the type is the hierarchy root.
static bool IsHierarchyRoot(Type t, IModel model)
{
    var et = model.FindEntityType(t);
    return et is not null && et.BaseType is null;
}

if (IsHierarchyRoot(typeof(Order), modelBuilder.Model))
    modelBuilder.Entity<Order>().HasPartitionKey(o => o.TenantId);

Prevention

When it happens

Trigger: Calling HasPartitionKey on a derived entity (an entity that participates in a TPH hierarchy as a non-root type). Fires during ValidateKeys at model validation time.

Common situations: Applying HasPartitionKey inside a per-derived-type IEntityTypeConfiguration; copying a config that worked for a standalone entity onto a hierarchy member; not realizing partition keys apply to the whole container.

Related errors


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

Appendix: 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 3a2006ef56)