App-vNext/Polly · error · ArgumentOutOfRangeException

Value must be greater than zero.

Error message

Value must be greater than zero.

What it means

Thrown by the legacy generic CircuitBreaker<TResult> syntax when 'handledEventsAllowedBeforeBreaking' is <= 0. This is the result-handling equivalent of the non-generic breaker: it counts both handled exceptions and handled results before opening. The ArgumentOutOfRangeException is raised at policy construction.

Source

Thrown at src/Polly/CircuitBreaker/CircuitBreakerTResultSyntax.cs:188

    /// </summary>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    /// <param name="policyBuilder">The policy builder.</param>
    /// <param name="handledEventsAllowedBeforeBreaking">The number of exceptions or handled results that are allowed before opening the circuit.</param>
    /// <param name="durationOfBreak">The duration the circuit will stay open before resetting.</param>
    /// <param name="onBreak">The action to call when the circuit transitions to an <see cref="CircuitState.Open"/> state.</param>
    /// <param name="onReset">The action to call when the circuit resets to a <see cref="CircuitState.Closed"/> state.</param>
    /// <param name="onHalfOpen">The action to call when the circuit transitions to <see cref="CircuitState.HalfOpen"/> state, ready to try action executions again. </param>
    /// <returns>The policy instance.</returns>
    /// <remarks>(see "Release It!" by Michael T. Nygard fi).</remarks>
    /// <exception cref="ArgumentOutOfRangeException">handledEventsAllowedBeforeBreaking;Value must be greater than zero.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onBreak"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onReset"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="onHalfOpen"/> is <see langword="null"/>.</exception>
    public static CircuitBreakerPolicy<TResult> CircuitBreaker<TResult>(this PolicyBuilder<TResult> policyBuilder, int handledEventsAllowedBeforeBreaking, TimeSpan durationOfBreak, Action<DelegateResult<TResult>, CircuitState, TimeSpan, Context> onBreak, Action<Context> onReset, Action onHalfOpen)
    {
        if (handledEventsAllowedBeforeBreaking <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(handledEventsAllowedBeforeBreaking), "Value must be greater than zero.");
        }

        if (durationOfBreak < TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(durationOfBreak), "Value must be greater than zero.");
        }

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

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

        if (onHalfOpen == null)

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Pass handledEventsAllowedBeforeBreaking >= 1.
  2. Validate/clamp the sourced value before building: var threshold = config.GetValue<int?>("Breaker:Threshold") ?? 5;.
  3. Confirm the parameter refers to handled events (results OR exceptions), not retries.

Example fix

// before
var policy = Policy<Result>
    .Handle<HttpException>()
    .OrResult(r => r.IsFailure)
    .CircuitBreaker(0, TimeSpan.FromSeconds(30), onBreak, onReset, onHalfOpen);

// after
var threshold = config.GetValue<int?>("Breaker:Threshold") ?? 5;
var policy = Policy<Result>
    .Handle<HttpException>()
    .OrResult(r => r.IsFailure)
    .CircuitBreaker(threshold, TimeSpan.FromSeconds(30), onBreak, onReset, onHalfOpen);
Defensive patterns

Strategy: validation

Validate before calling

if (handledEventsAllowedBeforeBreaking <= 0) throw new ArgumentOutOfRangeException(nameof(handledEventsAllowedBeforeBreaking));
var policy = Policy<TResult>.Handle<X>().CircuitBreaker(handledEventsAllowedBeforeBreaking, durationOfBreak, ...);

Type guard

static int NormalizeThreshold(int v) => v <= 0 ? 1 : v;

Try / catch

try { var policy = builder.CircuitBreaker(n, ts, ...); } catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "handledEventsAllowedBeforeBreaking") { /* surface config error */ throw; }

Prevention

When it happens

Trigger: Calling Policy<TResult>.Handle<...>().CircuitBreaker(handledEventsAllowedBeforeBreaking, durationOfBreak, onBreak, onReset, onHalfOpen) with handledEventsAllowedBeforeBreaking <= 0. Often hit when a threshold read from config is 0 or unset.

Common situations: Binding the threshold from IConfiguration with a missing key (resolves to 0); sharing a constant with a no-op policy that intentionally uses 0; misunderstanding that the count covers both faulted results and exceptions.

Related errors


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