App-vNext/Polly · error · ArgumentNullException

Value cannot be null. (Parameter 'onCacheMiss')

Error message

Value cannot be null. (Parameter 'onCacheMiss')

What it means

Thrown by the terminal CacheAsync<TResult>(...) overload when onCacheMiss is null. The miss callback is mandatory in this verbose overload; a null would NPE at miss time, so it is rejected at construction. Earlier guards (provider, TTL, key, onCacheGet) run before it.

Source

Thrown at src/Polly/Caching/AsyncCacheTResultSyntax.cs:1001

        if (ttlStrategy == null)
        {
            throw new ArgumentNullException(nameof(ttlStrategy));
        }

        if (cacheKeyStrategy == null)
        {
            throw new ArgumentNullException(nameof(cacheKeyStrategy));
        }

        if (onCacheGet == null)
        {
            throw new ArgumentNullException(nameof(onCacheGet));
        }

        if (onCacheMiss == null)
        {
            throw new ArgumentNullException(nameof(onCacheMiss));
        }

        if (onCachePut == null)
        {
            throw new ArgumentNullException(nameof(onCachePut));
        }

        return new AsyncCachePolicy<TResult>(cacheProvider, ttlStrategy, cacheKeyStrategy, onCacheGet, onCacheMiss, onCachePut, onCacheGetError, onCachePutError);
    }

    private static void EmptyCallback(Context context, string key)
    {
        // No-op
    }
}

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Pass a non-null Action<Context,string> (a no-op is acceptable).
  2. Use a simpler overload that injects EmptyCallback if you do not track misses.
  3. Null-check before the call.

Example fix

// before
var policy = Policy.CacheAsync<string>(provider, ttlStrategy, keyFunc, onGet, null, onPut, null, null);
// after
Action<Context, string> noop = (_, _) => { };
var policy = Policy.CacheAsync<string>(provider, ttlStrategy, keyFunc, onGet, noop, onPut, null, null);
Defensive patterns

Strategy: validation

Validate before calling

Action<Context, string> onMiss = onCacheMiss ?? ((_, _) => { });
var policy = Policy.CacheAsync<TResult>(provider, ttlStrategy, keyFunc, onGet, onMiss, onPut, null, null);

Type guard

static bool HasCallback(Action<Context, string>? a) => a is not null;

Try / catch

try { var p = Policy.CacheAsync<R>(provider, ttl, keyFunc, onGet, onMiss, onPut, null, null); }
catch (ArgumentNullException ex) when (ex.ParamName == "onCacheMiss") { /* supply a no-op callback */ }

Prevention

When it happens

Trigger: Calling the full overload with null for the onCacheMiss Action<Context,string>.

Common situations: Assuming the callback is optional; refactor omitted the assignment; test placeholder left null.

Related errors


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