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 the legacy Polly v7 synchronous Retry API (PolicyBuilder, exception-based) when retryCount is negative for the overload taking Action<Exception, int> onRetry. Polly treats a negative retry count as invalid because the count bounds how many times the delegate re-executes after a handled failure.

Source

Thrown at src/Polly/Retry/RetrySyntax.cs:50

    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onRetry"/> is <see langword="null"/>.</exception>
    public static RetryPolicy Retry(this PolicyBuilder policyBuilder, Action<Exception, int> onRetry) =>
        policyBuilder.Retry(1, onRetry);

    /// <summary>
    /// Builds a <see cref="Policy"/> that will retry <paramref name="retryCount"/> times
    /// calling <paramref name="onRetry"/> on each retry with the raised exception and retry count.
    /// </summary>
    /// <param name="policyBuilder">The policy builder.</param>
    /// <param name="retryCount">The retry count.</param>
    /// <param name="onRetry">The action to call 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="onRetry"/> is <see langword="null"/>.</exception>
    public static RetryPolicy Retry(this PolicyBuilder policyBuilder, int retryCount, Action<Exception, int> onRetry)
    {
        if (retryCount < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(retryCount), "Value must be greater than or equal to zero.");
        }

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

        return policyBuilder.Retry(retryCount, (outcome, i, _) => onRetry(outcome, i));
    }

    /// <summary>
    /// Builds a <see cref="Policy"/> that will retry once
    /// calling <paramref name="onRetry"/> on retry with the raised exception, retry count 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>

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Validate retryCount >= 0 before calling Retry; use 0 to run without retries.
  2. Correct the configuration so it cannot return a negative number.
  3. Add a startup guard that fails the app loudly on bad config instead of letting it reach Polly.

Example fix

// before
var policy = Policy.Handle<HttpRequestException>().Retry(_config.RetryCount, (ex, i) => Log(ex, i));
// after
if (_config.RetryCount < 0) throw new InvalidOperationException("RetryCount must be >= 0");
var policy = Policy.Handle<HttpRequestException>().Retry(_config.RetryCount, (ex, i) => Log(ex, i));
Defensive patterns

Strategy: validation

Validate before calling

if (retryCount < 0)
    throw new ArgumentOutOfRangeException(nameof(retryCount), "Retry count must be >= 0.");
var policy = Policy.Handle<TException>(pred).Retry(retryCount, onRetry);

Type guard

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

Prevention

When it happens

Trigger: Calling Policy.Handle<...>().Retry(retryCount, onRetry) with retryCount < 0 at RetrySyntax.cs:50.

Common situations: Reading retry count from appsettings where an unset value is read as -1; computing count from an arithmetic expression that underflows; passing a raw config int without bounds checking.

Related errors


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