App-vNext/Polly · error · ArgumentNullException

Value cannot be null. (Parameter 'cacheKeyStrategy')

Error message

Value cannot be null. (Parameter 'cacheKeyStrategy')

What it means

Thrown at policy-construction time by the legacy Polly v7 CachePolicy factory Policy.Cache(ISyncCacheProvider, TimeSpan, ICacheKeyStrategy, ...). The overload that takes a custom ICacheKeyStrategy requires it to be non-null because the policy derives every cache key from cacheKeyStrategy.GetCacheKey. Polly fails fast with ArgumentNullException rather than throwing a NullReferenceException later when a delegate executes.

Source

Thrown at src/Polly/Caching/CacheSyntax.cs:56

    /// If the <paramref name="cacheProvider "/> provides a value from cache, returns that value and does not execute the governed delegate.  If the <paramref name="cacheProvider" /> does not provide a value, executes the governed delegate, stores the value with the <paramref name="cacheProvider" />, then returns the value.
    /// </para>
    /// </summary>
    /// <param name="cacheProvider">The cache provider.</param>
    /// <param name="ttl">Duration (ttl) for which to cache values.</param>
    /// <param name="cacheKeyStrategy">The cache key strategy.</param>
    /// <param name="onCacheError">Delegate to call if an exception is thrown when attempting to get a value from or put a value into the cache, passing the execution context, the cache key, and the exception.</param>
    /// <returns>The policy instance.</returns>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="cacheProvider"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="cacheKeyStrategy"/> is <see langword="null"/>.</exception>
    public static CachePolicy Cache(
        ISyncCacheProvider cacheProvider,
        TimeSpan ttl,
        ICacheKeyStrategy cacheKeyStrategy,
        Action<Context, string, Exception>? onCacheError = null)
    {
        if (cacheKeyStrategy is null)
        {
            throw new ArgumentNullException(nameof(cacheKeyStrategy));
        }

        return Cache(cacheProvider, new RelativeTtl(ttl), cacheKeyStrategy.GetCacheKey, onCacheError);
    }

    /// <summary>
    /// <para>Builds a <see cref="Policy"/> that will function like a result cache for delegate executions returning a result.</para>
    /// <para>Before executing a delegate returning a result, checks whether the <paramref name="cacheProvider"/> holds a value for the cache key determined by applying the <paramref name="cacheKeyStrategy"/> to the execution <see cref="Context"/>.
    /// If the <paramref name="cacheProvider"/> provides a value from cache, returns that value and does not execute the governed delegate.  If the <paramref name="cacheProvider"/> does not provide a value, executes the governed delegate, stores the value with the <paramref name="cacheProvider"/>, then returns the value.
    /// </para>
    /// </summary>
    /// <param name="cacheProvider">The cache provider.</param>
    /// <param name="ttlStrategy">A strategy for specifying ttl for values to be cached.</param>
    /// <param name="cacheKeyStrategy">The cache key strategy.</param>
    /// <param name="onCacheError">Delegate to call if an exception is thrown when attempting to get a value from or put a value into the cache, passing the execution context, the cache key, and the exception.</param>
    /// <returns>The policy instance.</returns>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="cacheProvider"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="ttlStrategy"/> is <see langword="null"/>.</exception>

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Pass a non-null ICacheKeyStrategy instance to the overload.
  2. If you want default key derivation from Context.OperationKey, use the simpler overload Policy.Cache(cacheProvider, ttl) which supplies DefaultCacheKeyStrategy.Instance for you.
  3. Fix the DI registration / factory so the strategy is never null before the call.

Example fix

// before
Policy.Cache(provider, ttl, (ICacheKeyStrategy)null);
// after
Policy.Cache(provider, ttl); // uses DefaultCacheKeyStrategy (Context.OperationKey)
Defensive patterns

Strategy: validation

Validate before calling

if (cacheKeyStrategy is null) throw new InvalidOperationException("cacheKeyStrategy must be configured before building the cache policy.");
var policy = Policy.Cache(cacheProvider, ttl, cacheKeyStrategy);

Type guard

static bool IsKeyStrategyValid(Polly.Caching.ICacheKeyStrategy? s) => s is not null;

Prevention

When it happens

Trigger: Calling Policy.Cache(cacheProvider, ttl, (ICacheKeyStrategy)null) on the overload whose third parameter is ICacheKeyStrategy. A null reference (uninitialized field, a DI-resolved service that came back null, or a conditional factory that returned null) reaches that argument.

Common situations: DI container failed to register or inject the ICacheKeyStrategy; a custom key-strategy property is declared but never assigned; an overload-resolution mistake where you meant to pass a Func<Context,string> but the compiler bound to the ICacheKeyStrategy overload and you passed null.

Related errors


AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13). Data as JSON: /api/errors/8e9b64c9e4dfac71. Report an issue: GitHub.