App-vNext/Polly · error · ArgumentNullException

Value cannot be null.

Error message

Value cannot be null.

What it means

Thrown as ArgumentNullException(nameof(action)) from the non-generic AsyncCachePolicy.ImplementationAsync (the void-returning overload at line 41–54). This overload is a pass-through/NOOP for void executions through the cache policy — it does not actually cache anything, it just validates and invokes the action directly. The null guard at line 47–50 fires before invocation.

Solutions

  1. Ensure the async delegate passed to ExecuteAsync is non-null
  2. Guard the call site with a null check before invoking
  3. Use nullable reference types to surface null delegate issues at compile time

Example fix

// before
Func<CancellationToken, Task> doWork = GetWorkAsync(); // may be null
await cachePolicy.ExecuteAsync(doWork); // ArgumentNullException

// after
Func<CancellationToken, Task> doWork = GetWorkAsync();
if (doWork is not null)
{
    await cachePolicy.ExecuteAsync(doWork);
}
Defensive patterns

Strategy: validation

Validate before calling

if (action is null)
{
    throw new InvalidOperationException("Async action must be provided.");
}
await cachePolicy.ExecuteAsync(action);

Type guard

public static bool IsExecutableAsyncDelegate(Func<CancellationToken, Task> action) => action is not null;

Prevention

When it happens

Trigger: Calling policy.ExecuteAsync(null) on an AsyncCachePolicy where the delegate returns Task (void) rather than Task<TResult>. The pass-through ImplementationAsync at line 41 catches the null action before any cache interaction.

Common situations: Passing a null async delegate to a cache policy's fire-and-forget execution path. Refactoring that moved the delegate construction to a conditional path that returns null. Misunderstanding that void-returning cache executions are pass-through and not cached.

Related errors


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

Appendix: source

Thrown at src/Polly/Caching/AsyncCachePolicy.cs:49

        _cacheKeyStrategy = cacheKeyStrategy;

        _onCacheGet = onCacheGet;
        _onCachePut = onCachePut;
        _onCacheMiss = onCacheMiss;
        _onCacheGetError = onCacheGetError;
        _onCachePutError = onCachePutError;
    }

    /// <inheritdoc/>
    protected override Task ImplementationAsync(
        Func<Context, CancellationToken, Task> action,
        Context context,
        CancellationToken cancellationToken,
        bool continueOnCapturedContext)
    {
        if (action is null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        // Pass-through/NOOP policy action, for void-returning executions through the cache policy.
        return action(context, cancellationToken);
    }

    /// <inheritdoc/>
    [DebuggerStepThrough]
    protected override Task<TResult> ImplementationAsync<TResult>(
        Func<Context, CancellationToken, Task<TResult>> action,
        Context context,
        CancellationToken cancellationToken,
        bool continueOnCapturedContext)
    {
        if (action is null)
        {
            throw new ArgumentNullException(nameof(action));
        }

View on GitHub (pinned to d0e46bdb1e)