App-vNext/Polly · error · ArgumentNullException

sleepDurations

Error message

sleepDurations

What it means

ArgumentNullException is thrown when sleepDurations is null in the terminal WaitAndRetryAsync(IEnumerable<TimeSpan> sleepDurations, Func<Exception,TimeSpan,int,Context,Task> onRetryAsync) overload. All convenience overloads (errors 203-207) delegate here, so a null sleepDurations passed to any of them surfaces at this guard. The enumerable defines both the number of retries and the wait duration for each.

Solutions

  1. Supply an explicit list: new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4) }.
  2. If building from config, ensure the binding target is initialized and verify with a null check before calling the policy.
  3. Return Enumerable.Empty<TimeSpan>() instead of null from helper methods.

Example fix

// before
var durations = config.GetSection("Backoff").Get<IEnumerable<TimeSpan>>();
Policy.Handle<HttpRequestException>().WaitAndRetryAsync(durations, onRetry);

// after
var durations = config.GetSection("Backoff").Get<List<TimeSpan>>() ?? new List<TimeSpan> { TimeSpan.FromSeconds(1) };
Policy.Handle<HttpRequestException>().WaitAndRetryAsync(durations, onRetry);
Defensive patterns

Strategy: validation

Validate before calling

sleepDurations ??= new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4) };
if (!sleepDurations.Any())
    throw new InvalidOperationException("At least one sleep duration is required.");

Type guard

static bool IsValidDurations(IEnumerable<TimeSpan> durations)
    => durations is not null && durations.Any();

Prevention

When it happens

Trigger: Passing null where an IEnumerable<TimeSpan> is expected — typically from a configuration method that returned null, a LINQ query that produced no results and was then overwritten, or a factory that failed silently.

Common situations: A backoff schedule read from JSON config that deserialized to null because the section name was mistyped, or a helper method returning null instead of an empty enumerable.

Related errors


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

Appendix: source

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

#pragma warning restore 1998
    }

    /// <summary>
    ///     Builds an <see cref="AsyncRetryPolicy" /> that will wait and retry as many times as there are provided
    ///     <paramref name="sleepDurations" />
    ///     calling <paramref name="onRetryAsync" /> on each retry with the raised exception, the current sleep duration, retry count, and context data.
    ///     On each retry, the duration to wait is the current <paramref name="sleepDurations" /> item.
    /// </summary>
    /// <param name="policyBuilder">The policy builder.</param>
    /// <param name="sleepDurations">The sleep durations to wait for on each retry.</param>
    /// <param name="onRetryAsync">The action to call asynchronously on each retry.</param>
    /// <returns>The policy instance.</returns>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="sleepDurations"/> or <paramref name="onRetryAsync"/> is <see langword="null"/>.</exception>
    public static AsyncRetryPolicy WaitAndRetryAsync(this PolicyBuilder policyBuilder, IEnumerable<TimeSpan> sleepDurations, Func<Exception, TimeSpan, int, Context, Task> onRetryAsync)
    {
        if (sleepDurations == null)
        {
            throw new ArgumentNullException(nameof(sleepDurations));
        }

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

        return new AsyncRetryPolicy(
            policyBuilder,
            onRetryAsync,
            sleepDurationsEnumerable: sleepDurations);
    }

    /// <summary>
    /// Builds an <see cref="AsyncRetryPolicy"/> that will wait and retry indefinitely until the action succeeds.
    ///     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)