dotnet/reactive · error · ArgumentOutOfRangeException

Specified argument was out of the range of valid values…

Error message

Specified argument was out of the range of valid values. (Parameter 'capacity')

What it means

The capacity parameter bounds how many groups may exist concurrently and must be non-negative. Passing a negative value throws ArgumentOutOfRangeException at GroupByUntil.cs:57. This is a fail-fast range check applied when the capacity overload is invoked.

Solutions

  1. Clamp the capacity before the call: Math.Max(0, computedCapacity)
  2. Fix the config/arithmetic source producing the negative value
  3. Validate user- or config-supplied capacity at load time and reject negatives early

Example fix

// before
int capacity = config.MaxGroups - activeGroups; // can be negative
var groups = source.GroupByUntil(x => x.Key, g => g.Take(1), capacity);
// after
int capacity = Math.Max(0, config.MaxGroups - activeGroups);
var groups = source.GroupByUntil(x => x.Key, g => g.Take(1), capacity);
Defensive patterns

Strategy: validation

Validate before calling

if (capacity < 0) throw new ArgumentException("capacity must be >= 0"); // or clamp: capacity = Math.Max(0, capacity);

Type guard

bool IsValidCapacity(int capacity) => capacity >= 0;

Try / catch

try { var groups = source.GroupByUntil(ks, ds, capacity); } catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "capacity") { capacity = 0; /* retry or log bad config */ }

Prevention

When it happens

Trigger: Calling AsyncObservable.GroupByUntil(source, keySelector, durationSelector, -1) — typically a computed capacity like limit - consumed that went negative, or an uninitialized/default-initialized sentinel.

Common situations: Capacity derived from configuration where a negative or unparsed value slipped in; arithmetic like expectedCount - actualCount producing -1; int fields defaulted to -1 as 'unset'.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/8d7ab099cf54e6ca. Report an issue: GitHub.

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs:57

            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(
                source,
                (keySelector, durationSelector, comparer),
                static (source, state, observer) => GroupByUntilCore<TSource, TKey, TSource, TDuration>(source, observer, (o, d) => AsyncObserver.GroupByUntil(o, d, state.keySelector, state.durationSelector, state.comparer)));
        }

        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupByUntil<TSource, TKey, TDuration>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));

            return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(
                source,
                (keySelector, durationSelector, capacity),
                static (source, state, observer) => GroupByUntilCore<TSource, TKey, TSource, TDuration>(source, observer, (o, d) => AsyncObserver.GroupByUntil(o, d, state.keySelector, state.durationSelector, state.capacity)));
        }

        public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupByUntil<TSource, TKey, TDuration>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity, IEqualityComparer<TKey> comparer)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));
            if (comparer == null)

View on GitHub (pinned to 94b5d5ab91)