App-vNext/Polly · error · ArgumentNullException

sleepDurationProvider

Error message

sleepDurationProvider

What it means

ArgumentNullException is thrown when sleepDurationProvider is null in WaitAndRetryForeverAsync(Func<int,TimeSpan> sleepDurationProvider, Action<Exception,TimeSpan> onRetry). This function is the sole source of wait durations for the infinite retry policy and cannot be omitted.

Source

Thrown at src/Polly/Retry/AsyncRetrySyntax.cs:886

        => policyBuilder.WaitAndRetryForeverAsync(sleepDurationProvider, EmptyHandlerWithContext);

    /// <summary>
    /// Builds an <see cref="AsyncRetryPolicy"/> that will wait and retry indefinitely
    /// calling <paramref name="onRetry"/> on each retry with the raised exception.
    ///     On each retry, the duration to wait is calculated by calling <paramref name="sleepDurationProvider" /> with
    ///     the current retry number (1 for first retry, 2 for second etc).
    /// </summary>
    /// <param name="policyBuilder">The policy builder.</param>
    /// <param name="sleepDurationProvider">A function providing the duration to wait before retrying.</param>
    /// <param name="onRetry">The action to call on each retry.</param>
    /// <returns>The policy instance.</returns>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="sleepDurationProvider"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onRetry"/> is <see langword="null"/>.</exception>
    public static AsyncRetryPolicy WaitAndRetryForeverAsync(this PolicyBuilder policyBuilder, Func<int, TimeSpan> sleepDurationProvider, Action<Exception, TimeSpan> onRetry)
    {
        if (sleepDurationProvider == null)
        {
            throw new ArgumentNullException(nameof(sleepDurationProvider));
        }

        if (onRetry == null)
        {
            throw new ArgumentNullException(nameof(onRetry));
        }

        return policyBuilder.WaitAndRetryForeverAsync(
            (retryCount, _) => sleepDurationProvider(retryCount),
            (exception, timespan, _) => onRetry(exception, timespan));
    }

    /// <summary>
    /// Builds an <see cref="AsyncRetryPolicy"/> that will wait and retry indefinitely
    /// calling <paramref name="onRetry"/> on each retry with the raised exception and retry count.
    ///     On each retry, the duration to wait is calculated by calling <paramref name="sleepDurationProvider" /> with
    ///     the current retry number (1 for first retry, 2 for second etc).
    /// </summary>

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Supply a non-null lambda: retryAttempt => TimeSpan.FromSeconds(retryAttempt).
  2. Verify DI registration of the provider before building the policy.
  3. Use a named local function instead of a variable to guarantee non-null.

Example fix

// before
.WaitAndRetryForeverAsync(null, onRetry)

// after
.WaitAndRetryForeverAsync(retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), onRetry)
Defensive patterns

Strategy: validation

Validate before calling

if (sleepDurationProvider is null)
    throw new InvalidOperationException("sleepDurationProvider must be configured.");

Type guard

static bool IsValidProvider(Func<int, TimeSpan> provider) => provider is not null;

Prevention

When it happens

Trigger: Calling WaitAndRetryForeverAsync with a null sleepDurationProvider — typically from a factory or DI container that did not resolve the delegate.

Common situations: DI registration missing for the provider delegate, or a conditional assignment that left the variable null on an unhandled branch.

Related errors


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