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 AsyncRetrySyntax.RetryAsync(this PolicyBuilder, retryCount, Func<Exception,int,Context,Task> onRetryAsync) when retryCount is less than zero. A negative retry count is nonsensical; zero means no retries (fail fast) and is the valid floor.
Source
Thrown at src/Polly/Retry/AsyncRetrySyntax.cs:151
onRetryAsync: async (outcome, i, ctx) => onRetry(outcome, i, ctx));
#pragma warning restore 1998
}
/// <summary>
/// Builds an <see cref="AsyncRetryPolicy"/> that will retry <paramref name="retryCount"/> times
/// calling <paramref name="onRetryAsync"/> on each retry with the raised exception, retry count and context data.
/// </summary>
/// <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 RetryAsync(this PolicyBuilder policyBuilder, int retryCount, Func<Exception, 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(
policyBuilder,
(outcome, _, i, ctx) => onRetryAsync(outcome, i, ctx),
retryCount);
}
/// <summary>
/// Builds an <see cref="AsyncRetryPolicy" /> that will retry indefinitely until the action succeeds.
/// </summary>
/// <param name="policyBuilder">The policy builder.</param>
/// <returns>The policy instance.</returns>View on GitHub (pinned to d0e46bdb1e)
Solutions
- Set retryCount to 0 or a positive integer (0 = no retries).
- Clamp the config value with Math.Max(0, configuredValue) at load time.
- Use a distinct sentinel and map it to a default retry count rather than passing it through.
Example fix
// before var policy = Policy.Handle<HttpRequestException>().RetryAsync(-1, onRetryAsync); // after var policy = Policy.Handle<HttpRequestException>().RetryAsync(3, onRetryAsync);
Defensive patterns
Strategy: validation
Validate before calling
var retryCount = Math.Max(0, config.GetValue("Retry:Count", 3));
var policy = builder.RetryAsync(retryCount, onRetryAsync); Prevention
- Clamp config-driven retry counts with Math.Max(0, value) at load.
- Use 0 to mean fail-fast (no retries) rather than a negative sentinel.
- Validate retry config via options-pattern Validate() to fail at startup.
When it happens
Trigger: Calling .RetryAsync(retryCount: -1, ...) on the context-aware async-callback overload; any negative integer for retryCount.
Common situations: Config-driven retry count defaulting to -1 (sentinel for "unset"); arithmetic that subtracts from retry count; env var parsed as a negative number.
Related errors
- numberOfExecutions per timespan must be an integer greater t
- No predicates were configured. There must be at least one pr
- {PrimaryMessage}\nValidation Errors:\n{error.ErrorMessage}
- Value must be greater than zero.
- Value must be greater than or equal to zero.
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/9e25fdd356279396.
Report an issue: GitHub.