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.

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)

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.