App-vNext/Polly · error · ArgumentOutOfRangeException

numberOfExecutions per timespan must be an integer greater t

Error message

numberOfExecutions per timespan must be an integer greater than or equal to 1.

What it means

Thrown by the typed RateLimitAsync<TResult>(int, TimeSpan, int, Func<TimeSpan,Context,TResult>?) overload when numberOfExecutions is less than 1. Identical validation to the factory-less overload; the typed (TResult-locked) policy applies the same lower bound on executions per window.

Source

Thrown at src/Polly/RateLimit/AsyncRateLimitTResultSyntax.cs:68

    /// with a maximum burst size of <paramref name="maxBurst"/>.
    /// </summary>
    /// <typeparam name="TResult">The type of return values this policy will handle.</typeparam>
    /// <param name="numberOfExecutions">The number of executions (call it N) permitted per timespan.</param>
    /// <param name="perTimeSpan">How often N executions are permitted.</param>
    /// <param name="maxBurst">The maximum number of executions that will be permitted in a single burst (for example if none have been executed for a while).
    /// This equates to the bucket-capacity of a token-bucket implementation.</param>
    /// <param name="retryAfterFactory">An (optional) factory to use to express retry-after back to the caller, when an operation is rate-limited.
    /// <remarks>If null, a <see cref="RateLimitRejectedException"/> with property <see cref="RateLimitRejectedException.RetryAfter"/> will be thrown to indicate rate-limiting.</remarks></param>
    /// <returns>The policy instance.</returns>
    public static AsyncRateLimitPolicy<TResult> RateLimitAsync<TResult>(
        int numberOfExecutions,
        TimeSpan perTimeSpan,
        int maxBurst,
        Func<TimeSpan, Context, TResult>? retryAfterFactory)
    {
        if (numberOfExecutions < 1)
        {
            throw new ArgumentOutOfRangeException(nameof(numberOfExecutions), numberOfExecutions, $"{nameof(numberOfExecutions)} per timespan must be an integer greater than or equal to 1.");
        }

        if (perTimeSpan <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(perTimeSpan), perTimeSpan, $"{nameof(perTimeSpan)} must be a positive timespan.");
        }

        if (maxBurst < 1)
        {
            throw new ArgumentOutOfRangeException(nameof(maxBurst), maxBurst, $"{nameof(maxBurst)} must be an integer greater than or equal to 1.");
        }

        var onePer = TimeSpan.FromTicks(perTimeSpan.Ticks / numberOfExecutions);

        if (onePer <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(perTimeSpan), perTimeSpan, "The number of executions per timespan must be positive.");
        }

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Set numberOfExecutions to a positive integer matching measured load.
  2. Clamp config: Math.Max(1, parsed); reject non-positive values at the config layer.
  3. To disable rate limiting, skip policy construction rather than passing 0.
  4. Add a config validation step at startup that asserts all three numeric bounds.

Example fix

// before
var p = Policy.RateLimitAsync<int>(0, TimeSpan.FromSeconds(1), 10, null);

// after
var p = Policy.RateLimitAsync<int>(10, TimeSpan.FromSeconds(1), 10, null);
Defensive patterns

Strategy: validation

Validate before calling

int n = Math.Max(1, parsedNumberOfExecutions);
var p = Policy.RateLimitAsync<TResult>(n, perTimeSpan, maxBurst, retryAfterFactory);

Prevention

When it happens

Trigger: Calling Policy.RateLimitAsync<TResult>(0, ts, burst, factory) or with a negative numberOfExecutions. The factory argument does not affect this guard.

Common situations: Config-driven numberOfExecutions that parses to 0 (missing key), a feature flag setting the limit to 0 to 'disable', or mis-scaled units from configuration.

Related errors


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