dotnet/reactive · error · ArgumentOutOfRangeException

ArgumentOutOfRangeException(nameof(maxConcurrent))

Error message

ArgumentOutOfRangeException(nameof(maxConcurrent))

What it means

Observable.Merge(sources, maxConcurrent) throws ArgumentOutOfRangeException when maxConcurrent is less than or equal to zero. Concurrency limits of 0 or negative values are meaningless (at least one inner stream must be allowed), so the library rejects them eagerly after the null check on sources.

Solutions

  1. Pass a positive maxConcurrent value (>= 1), e.g. Math.Max(1, configuredLimit).
  2. If 0 means 'no limit', call the parameterless Merge() overload instead.
  3. Validate the config value at load time and fail with a clear message before building the pipeline.

Example fix

// before
var merged = streams.Merge(maxConcurrentFromConfig); // may be 0

// after
var merged = maxConcurrentFromConfig > 0
    ? streams.Merge(maxConcurrentFromConfig)
    : streams.Merge(); // treat 0 as unlimited
Defensive patterns

Strategy: validation

Validate before calling

if (maxConcurrent <= 0)
    throw new ArgumentOutOfRangeException(nameof(maxConcurrent), "Must be greater than zero");
var merged = sources.Merge(maxConcurrent);

Type guard

bool IsValidConcurrency(int max) => max > 0;

Try / catch

try { var merged = sources.Merge(maxConcurrent); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "maxConcurrent") { log.Error("maxConcurrent must be > 0, got {0}", maxConcurrent); throw; }

Prevention

When it happens

Trigger: Calling sources.Merge(0) or sources.Merge(-1); passing a computed limit from config (e.g. 'maxConcurrentConnections': 0) or an uninitialized int defaulting to 0.

Common situations: Config files where 0 was intended to mean 'unlimited' but the API expects a positive integer; parsing an empty/invalid setting into default(int) = 0; off-by-one in a derived limit.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Multiple.cs:494

        /// <summary>
        /// Merges elements from all inner observable sequences into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequences.</typeparam>
        /// <param name="sources">Observable sequence of inner observable sequences.</param>
        /// <param name="maxConcurrent">Maximum number of inner observable sequences being subscribed to concurrently.</param>
        /// <returns>The observable sequence that merges the elements of the inner sequences.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="sources"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxConcurrent"/> is less than or equal to zero.</exception>
        public static IObservable<TSource> Merge<TSource>(this IObservable<IObservable<TSource>> sources, int maxConcurrent)
        {
            if (sources == null)
            {
                throw new ArgumentNullException(nameof(sources));
            }

            if (maxConcurrent <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(maxConcurrent));
            }

            return s_impl.Merge(sources, maxConcurrent);
        }

        /// <summary>
        /// Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequences.</typeparam>
        /// <param name="sources">Enumerable sequence of observable sequences.</param>
        /// <param name="maxConcurrent">Maximum number of observable sequences being subscribed to concurrently.</param>
        /// <returns>The observable sequence that merges the elements of the observable sequences.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="sources"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxConcurrent"/> is less than or equal to zero.</exception>
        public static IObservable<TSource> Merge<TSource>(this IEnumerable<IObservable<TSource>> sources, int maxConcurrent)
        {
            if (sources == null)
            {

View on GitHub (pinned to 94b5d5ab91)