App-vNext/Polly · error · ArgumentNullException
Value cannot be null.
Error message
Value cannot be null.
What it means
Thrown by AsyncRetryPolicy.ImplementationAsync<TResult> (the generic, exception-predicate-based legacy retry policy) when the action delegate is null. The retry engine needs a delegate to invoke on each attempt.
Source
Thrown at src/Polly/Retry/AsyncRetryPolicy.cs:38
{
_permittedRetryCount = permittedRetryCount;
_sleepDurationsEnumerable = sleepDurationsEnumerable;
_sleepDurationProvider = sleepDurationProvider;
_onRetryAsync = onRetryAsync;
}
/// <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));
}
var sleepDurationProvider = _sleepDurationProvider != null
? (retryCount, outcome, ctx) => _sleepDurationProvider(retryCount, outcome.Exception, ctx)
: (Func<int, DelegateResult<TResult>, Context, TimeSpan>)null;
return AsyncRetryEngine.ImplementationAsync(
action,
context,
ExceptionPredicates,
ResultPredicates<TResult>.None,
(outcome, timespan, retryCount, ctx) => _onRetryAsync(outcome.Exception, timespan, retryCount, ctx),
cancellationToken,
_permittedRetryCount,
_sleepDurationsEnumerable,
sleepDurationProvider,
continueOnCapturedContext);
}View on GitHub (pinned to d0e46bdb1e)
Solutions
- Pass a non-null Func<Context, CancellationToken, Task<TResult>>.
- Verify the action variable is assigned before ExecuteAsync.
- Use a no-op delegate returning default(TResult) if a placeholder is needed.
Example fix
// before await policy.ExecuteAsync(null, context, ct); // after await policy.ExecuteAsync(_ => Task.FromResult(result), context, ct);
Defensive patterns
Strategy: validation
Validate before calling
if (action is null) throw new ArgumentException("action must be set before executing the retry policy.");
await policy.ExecuteAsync(action, context, cancellationToken); Type guard
static bool IsActionSet<TResult>(Func<Context, CancellationToken, Task<TResult>> action) => action is not null;
Prevention
- Pass a verified non-null delegate to ExecuteAsync.
- Avoid conditional action assignment without a fallback.
- Enable nullable reference types to flag null delegates at compile time.
When it happens
Trigger: Calling ExecuteAsync with a null action on an AsyncRetryPolicy built via the legacy RetryAsync exception-based syntax. The protected ImplementationAsync is reached through the base ExecuteAsync path.
Common situations: Passing a method-group that resolved to null; an action selected conditionally that stayed null; refactor that dropped the delegate argument.
Related errors
- Value cannot be null.
- onRetryAsync
- Value must be greater than or equal to zero.
- onRetry
- Value must be greater than or equal to zero.
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/84fc966b92e6486d.
Report an issue: GitHub.