App-vNext/Polly · error · ArgumentNullException

Value cannot be null. (Parameter 'action')

Error message

Value cannot be null. (Parameter 'action')

What it means

ArgumentNullException thrown by AsyncNoOpPolicy (non-generic).ImplementationAsync when the action delegate is null. NoOp passes the delegate straight through to NoOpEngine, so a null action cannot be invoked. The guard fires when the policy's Execute path is entered with a null func.

Source

Thrown at src/Polly/NoOp/AsyncNoOpPolicy.cs:20

namespace Polly.NoOp;

/// <summary>
/// A noop policy that can be applied to asynchronous delegates.
/// </summary>
public class AsyncNoOpPolicy : AsyncPolicy, INoOpPolicy
{
    internal AsyncNoOpPolicy()
    {
    }

    /// <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));
        }

        return NoOpEngine.ImplementationAsync(action, context, cancellationToken);
    }
}

/// <summary>
/// A noop policy that can be applied to asynchronous delegates returning a value of type <typeparamref name="TResult"/>.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
public class AsyncNoOpPolicy<TResult> : AsyncPolicy<TResult>, INoOpPolicy<TResult>
{
    internal AsyncNoOpPolicy()
    {
    }

    /// <inheritdoc/>
    [DebuggerStepThrough]

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Pass a non-null action delegate to ExecuteAsync.
  2. Guard the delegate before calling: if (action is not null) await policy.ExecuteAsync(action).
  3. Check DI registrations resolve a real delegate, not null.

Example fix

// before
Func<CancellationToken, Task<int>> action = GetAction(); // may be null
var result = await noOpPolicy.ExecuteAsync(action, ct);

// after
var action = GetAction() ?? (_ => Task.FromResult(0));
var result = await noOpPolicy.ExecuteAsync(action, ct);
Defensive patterns

Strategy: validation

Validate before calling

if (action is null) throw new ArgumentNullException(nameof(action));
await Policy.NoOpAsync().ExecuteAsync(action, ctx, ct);

Type guard

static Func<Context, CancellationToken, Task<T>> EnsureAction<T>(Func<Context, CancellationToken, Task<T>> a) => a ?? ((_, _) => Task.FromResult<T>(default));

Try / catch

try { await noOpPolicy.ExecuteAsync(action, ct); } catch (ArgumentNullException ex) when (ex.ParamName == "action") { /* supply delegate */ throw; }

Prevention

When it happens

Trigger: Calling policy.ExecuteAsync(null) or passing a null Func<Context, CancellationToken, Task<TResult>> into an AsyncNoOpPolicy obtained via Policy.NoOpAsync().

Common situations: A null delegate resolved from DI/optional config; refactor that changed the delegate signature leaving a null reference; tests asserting no-op behavior but forgetting to supply a delegate.

Related errors


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