App-vNext/Polly · error · ArgumentNullException
onRetryAsync
Error message
onRetryAsync
What it means
Thrown by RetryAsync<TResult> when the async callback Func<DelegateResult<TResult>, int, Task> onRetryAsync is null. This overload forwards to the context-aware variant with a discarded context; it only null-checks onRetryAsync and delegates retryCount validation downstream. The callback is invoked and awaited on each retry, so null is rejected at construction.
Source
Thrown at src/Polly/Retry/AsyncRetryTResultSyntax.cs:92
#pragma warning restore 1998
}
/// <summary>
/// Builds an <see cref="AsyncRetryPolicy{TResult}" /> that will retry <paramref name="retryCount" /> times
/// calling <paramref name="onRetryAsync" /> on each retry with the handled exception or result and retry count.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="policyBuilder">The policy builder.</param>
/// <param name="retryCount">The retry count.</param>
/// <param name="onRetryAsync">The action to call asynchronously on each retry.</param>
/// <returns>The policy instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">retryCount;Value must be greater than or equal to zero.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="onRetryAsync"/> is <see langword="null"/>.</exception>
public static AsyncRetryPolicy<TResult> RetryAsync<TResult>(this PolicyBuilder<TResult> policyBuilder, int retryCount, Func<DelegateResult<TResult>, int, Task> onRetryAsync)
{
if (onRetryAsync == null)
{
throw new ArgumentNullException(nameof(onRetryAsync));
}
return policyBuilder.RetryAsync(retryCount, onRetryAsync: (outcome, i, _) => onRetryAsync(outcome, i));
}
/// <summary>
/// Builds an <see cref="AsyncRetryPolicy{TResult}"/> that will retry once
/// calling <paramref name="onRetry"/> on retry with the handled exception or result, retry count and context data.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="policyBuilder">The policy builder.</param>
/// <param name="onRetry">The action to call on each retry.</param>
/// <returns>The policy instance.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="onRetry"/> is <see langword="null"/>.</exception>
public static AsyncRetryPolicy<TResult> RetryAsync<TResult>(this PolicyBuilder<TResult> policyBuilder, Action<DelegateResult<TResult>, int, Context> onRetry) =>
policyBuilder.RetryAsync(1, onRetry);
/// <summary>View on GitHub (pinned to d0e46bdb1e)
Solutions
- Pass a non-null Func<DelegateResult<TResult>, int, Task>.
- Coalesce the optional argument: onRetryAsync ?? NoOpRetryAsync.
- Pick a parameterless-onRetry overload if no hook is required.
Example fix
// before .RetryAsync(3, onRetryAsync: null); // after .RetryAsync(3, onRetryAsync: async (outcome, i) => await Log.RetryAsync(outcome, i));
Defensive patterns
Strategy: validation
Validate before calling
if (onRetryAsync is null) throw new InvalidOperationException("onRetryAsync required.");
// then call RetryAsync<TResult>(retryCount, onRetryAsync) Type guard
static bool IsValidOnRetry(Func<DelegateResult<TResult>, int, Task> f) => f is not null;
Try / catch
try { Policy.HandleResult<T>(IsHandled).RetryAsync(retryCount, onRetryAsync); }
catch (ArgumentNullException ex) when (ex.ParamName == nameof(onRetryAsync))
{ /* supply a completed-task no-op callback */ } Prevention
- Default optional async hooks to a completed-task no-op.
- Register async retry callbacks in DI as always-resolved services.
- Turn on NRTs to catch null Func assignments at compile time.
When it happens
Trigger: Calling RetryAsync<TResult>(retryCount, onRetryAsync: null), or accepting an optional Func from a wrapper and passing it through unguarded.
Common situations: Wrapper that exposes an optional async retry hook, or DI resolving the callback as null.
Related errors
- onRetry
- Value cannot be null.
- Value must be greater than or equal to zero.
- Value cannot be null.
- onRetry
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/6e7c86b648a379e7.
Report an issue: GitHub.