dotnet/efcore · error · InvalidOperationException

'WithPartitionKey' can only be called once in a query. See…

Error message

'WithPartitionKey' can only be called once in a query. See https://aka.ms/efdocs-cosmos-partition-keys for more information.

What it means

CosmosQueryableExtensions.WithPartitionKey records partition key values on the QueryCompilationContext. The visitor checks PartitionKeyPropertyValues.Count before processing and, if it is already populated, throws InvalidOperationException — partition key can only be fixed once per query.

Solutions

  1. Call WithPartitionKey exactly once per query lifecycle.
  2. Centralize partition key injection in one helper and guard against double-application.
  3. Build the query from a single root, applying WithPartitionKey at the start, before any further composition.

Example fix

// before
var q = db.Users.WithPartitionKey("tenantA").Where(u => u.Active).WithPartitionKey("tenantA");
// after
var q = db.Users.WithPartitionKey("tenantA").Where(u => u.Active);
Defensive patterns

Strategy: validation

Validate before calling

static IQueryable<T> PartitionOnce<T>(IQueryable<T> q, params object[] keys)
    where T : class
{
    if (q is IQueryable<T> already && already.Provider.GetType().Name.Contains("Cosmos"))
    {
        // call WithPartitionKey only on a fresh DbSet root; track via a marker extension if needed
    }
    return q.WithPartitionKey(keys);
}

Prevention

When it happens

Trigger: Calling .WithPartitionKey(...) twice on the same IQueryable, or composing two queryables each of which already carries a WithPartitionKey call.

Common situations: Reusable query filters that bake in WithPartitionKey being combined with an ad-hoc per-call WithPartitionKey, or multi-tenant helpers that set the partition key and the caller also sets it.

Related errors


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

Appendix: source

Thrown at src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs:170

                        translatedResponseContinuationTokenLimitInKb,
                        typeof(CosmosPage<>).MakeGenericType(shapedQuery.ShaperExpression.Type)))
                .UpdateResultCardinality(ResultCardinality.Single);
        }

        return base.Translate(expression);
    }

    /// <inheritdoc />
    protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
    {
        var method = methodCallExpression.Method;

        if (methodCallExpression.Method.DeclaringType == typeof(CosmosQueryableExtensions)
            && methodCallExpression.Method.Name == nameof(CosmosQueryableExtensions.WithPartitionKey))
        {
            if (_queryCompilationContext.PartitionKeyPropertyValues.Count > 0)
            {
                throw new InvalidOperationException(CosmosStrings.WithPartitionKeyAlreadyCalled);
            }

            if (methodCallExpression.Arguments[0] is not EntityQueryRootExpression)
            {
                throw new InvalidOperationException(CosmosStrings.WithPartitionKeyBadNode);
            }

            var innerQueryable = Visit(methodCallExpression.Arguments[0]);

            for (var i = 1; i < methodCallExpression.Arguments.Count; i++)
            {
                var value = _sqlTranslator.Translate(methodCallExpression.Arguments[i], applyDefaultTypeMapping: false);
                if (value is not SqlConstantExpression and not SqlParameterExpression)
                {
                    throw new InvalidOperationException(CosmosStrings.WithPartitionKeyNotConstantOrParameter);
                }

                _queryCompilationContext.PartitionKeyPropertyValues.Add(value);

View on GitHub (pinned to 3a2006ef56)