App-vNext/Polly · error · ArgumentNullException
onRetry
Error message
onRetry
What it means
Thrown by the legacy Polly v7 synchronous RetryForever API when the onRetry callback (Action<Exception, int>) is null. RetryForever wires the callback into the policy so it fires on every retry indefinitely; a null callback would NRE on the first retry, so Polly validates it at construction.
Source
Thrown at src/Polly/Retry/RetrySyntax.cs:138
throw new ArgumentNullException(nameof(onRetry));
}
return policyBuilder.RetryForever((Exception outcome, Context _) => onRetry(outcome));
}
/// <summary>
/// Builds a <see cref="Policy"/> that will retry indefinitely
/// calling <paramref name="onRetry"/> on each retry with the raised exception and retry count.
/// </summary>
/// <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 RetryPolicy RetryForever(this PolicyBuilder policyBuilder, Action<Exception, int> onRetry)
{
if (onRetry == null)
{
throw new ArgumentNullException(nameof(onRetry));
}
return policyBuilder.RetryForever((outcome, i, _) => onRetry(outcome, i));
}
/// <summary>
/// Builds a <see cref="Policy"/> that will retry indefinitely
/// calling <paramref name="onRetry"/> on each retry with the raised exception and context data.
/// </summary>
/// <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 RetryPolicy RetryForever(this PolicyBuilder policyBuilder, Action<Exception, Context> onRetry)
{
if (onRetry == null)
{
throw new ArgumentNullException(nameof(onRetry));View on GitHub (pinned to d0e46bdb1e)
Solutions
- Provide a non-null Action<Exception, int>, e.g. (ex, retryCount) => _logger.Error(ex, "Retry {RetryCount}", retryCount).
- Use the parameterless RetryForever overload if you do not need a per-retry callback.
- Initialize handler fields to a no-op action instead of null.
Example fix
// before
var policy = Policy.Handle<SocketException>().RetryForever(null);
// after
var policy = Policy.Handle<SocketException>().RetryForever((ex, retryCount) => _logger.Warning(ex, "Retry {Count}", retryCount)); Defensive patterns
Strategy: validation
Validate before calling
if (onRetry is null)
throw new InvalidOperationException("onRetry callback is required.");
var policy = Policy.Handle<TException>(pred).RetryForever(onRetry); Type guard
static bool IsValid(Action<Exception, int>? cb) => cb is not null;
Prevention
- Always pass a non-null callback; use the parameterless RetryForever overload otherwise.
- Default handler fields to a no-op action.
- Enable nullable reference types.
When it happens
Trigger: Calling Policy.Handle<...>().RetryForever(null) with null for the Action<Exception, int> parameter at RetrySyntax.cs:138.
Common situations: Forgetting to pass the callback; passing a handler variable that is null; refactoring that removed the logging body; intending the parameterless RetryForever overload.
Related errors
- sleepDurationProvider
- Value must be greater than or equal to zero.
- Value cannot be null.
- Value cannot be null. (Parameter 'cacheProvider')
- Value cannot be null. (Parameter 'cacheKeyStrategy')
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/77f4014c0db52ecb.
Report an issue: GitHub.