dotnet/efcore · error · InvalidOperationException

The partition key value is of type

Error message

The partition key value is of type '{valueType}' which is not valid for Cosmos partition keys. All partition key properties values must be numeric, Boolean, or string, or converted to one of these types.

What it means

PartitionKeyBuilderExtensions.Add throws InvalidOperationException(CosmosStrings.PartitionKeyBadValue) when the value passed (after the property's value converter runs) is neither a string, a bool, nor a numeric type. Cosmos partition keys only support those primitive CLR categories; anything else (Guid, DateTime, enum-as-object, byte[], custom struct) hits the default switch arm and is rejected.

Solutions

  1. Configure a value converter on the partition key property that maps it to string, bool, or a numeric type (e.g. Guid -> string via ToString).
  2. Use a partition key property whose ClrType is already string, bool, int, long, double, decimal, etc.
  3. If the value is an enum, make sure the property type is the enum (so Add can convert it to its numeric value) rather than passing the boxed enum as object.
  4. Validate the value type before calling Add and reject or coerce unsupported types in your own code.

Example fix

// before - Guid partition key value throws
builder.Add(myGuid, partitionKeyProperty);

// after - convert the property to string at the model level
entity.Property(e => e.Id)
    .HasConversion(g => g.ToString(), s => Guid.Parse(s));
// or convert at the call site
builder.Add(myGuid.ToString(), partitionKeyProperty);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the partition key value before building it.
static bool IsSupportedPartitionKeyType(object? value)
    => value is null || value is string || value is bool || value.GetType().IsNumeric();

if (!IsSupportedPartitionKeyType(value))
    throw new ArgumentException($"Unsupported partition key type {value?.GetType()}");
builder.Add(value, property);

Type guard

static bool IsSupportedPartitionKeyType(Type t)
    => t == typeof(string) || t == typeof(bool) || t.IsNumeric();

Prevention

When it happens

Trigger: Calling PartitionKeyBuilder.Add with a Guid, DateTimeOffset, byte[], or custom struct value where a partition key is expected; a value converter that produces a non-numeric/non-string/non-bool provider value; an enum value boxed as object that did not get converted to its underlying numeric; passing a property whoseClrType is not one of the supported categories and has no converter.

Common situations: Modeling a partition key on a Guid or DateTime property without a value converter to string/numeric; using a custom struct for partition keys; converters that emit unsupported provider types; runtime data that drifts type from the configured property type.

Related errors


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

Appendix: source

Thrown at src/EFCore.Cosmos/Extensions/Internal/PartitionKeyBuilderExtensions.cs:78

                    if (expectedType != null && expectedType != typeof(bool))
                    {
                        CheckType(typeof(bool));
                    }

                    builder.Add(boolValue);
                    break;

                case var _ when value.GetType().IsNumeric():
                    if (expectedType != null && !expectedType.IsNumeric())
                    {
                        CheckType(value.GetType());
                    }

                    builder.Add(Convert.ToDouble(value));
                    break;

                default:
                    throw new InvalidOperationException(CosmosStrings.PartitionKeyBadValue(value.GetType()));
            }

            void CheckType(Type actualType)
            {
                if (expectedType != null && expectedType != actualType)
                {
                    throw new InvalidOperationException(
                        CosmosStrings.PartitionKeyBadValueType(
                            expectedType.ShortDisplayName(),
                            property!.DeclaringType.DisplayName(),
                            property.Name,
                            actualType.DisplayName()));
                }
            }
        }

        return builder;
    }

View on GitHub (pinned to 3a2006ef56)