App-vNext/Polly · error · ArgumentOutOfRangeException

Value must be greater than or equal to zero.

Error message

Value must be greater than or equal to zero.

What it means

Thrown by RetryAsync<TResult> when retryCount is negative (the guard is retryCount < 0). The message says 'Value must be greater than or equal to zero' — note the XML doc incorrectly says 'greater than zero', but zero retries (i.e. no retries) is actually permitted. This overload is the terminal context-aware async variant; it validates retryCount before onRetryAsync. A negative count has no semantic meaning for a retry policy.

Source

Thrown at src/Polly/Retry/AsyncRetryTResultSyntax.cs:161

#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, retry count and context data.
    /// </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 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, Context, Task> onRetryAsync)
    {
        if (retryCount < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(retryCount), "Value must be greater than or equal to zero.");
        }

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

        return new AsyncRetryPolicy<TResult>(
            policyBuilder,
            (outcome, _, i, ctx) => onRetryAsync(outcome, i, ctx),
            retryCount);
    }

    /// <summary>
    ///     Builds an <see cref="AsyncRetryPolicy{TResult}" /> that will retry indefinitely until the action succeeds.
    /// </summary>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    /// <param name="policyBuilder">The policy builder.</param>

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Clamp the value before the call: Math.Max(0, retryCount).
  2. Validate config at startup and fail with a clear message if retryCount < 0.
  3. Note that 0 is valid (policy never retries); only negative throws.

Example fix

// before
Policy.HandleResult<T>(IsHandled).RetryAsync(retryCountFromConfig, onRetryAsync);
// after
var retryCount = Math.Max(0, retryCountFromConfig);
Policy.HandleResult<T>(IsHandled).RetryAsync(retryCount, onRetryAsync);
Defensive patterns

Strategy: validation

Validate before calling

if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), "retryCount must be >= 0");
// safe retryCount is Math.Max(0, retryCount) if a default of 0 is acceptable

Type guard

static bool IsValidRetryCount(int count) => count >= 0;

Try / catch

try { Policy.HandleResult<T>(IsHandled).RetryAsync(retryCount, onRetryAsync); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == nameof(retryCount))
{ retryCount = 0; /* rebuild with a valid count */ }

Prevention

When it happens

Trigger: Passing a literal negative number, computing retryCount from config that defaulted to -1, or subtracting from a count and going below zero (e.g. retryCount - 1 when retryCount is 0).

Common situations: Config value missing and parsed to a sentinel like -1, arithmetic underflow in a derived count, or an environment-specific override that was never validated.

Related errors


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