dotnet/reactive · error · ArgumentNullException

source

Error message

source

What it means

Merge's public extension throws ArgumentNullException when the source observable of observables is null. Merge flattens a stream of inner streams into one; with no outer source there is nothing to subscribe. The check runs before Create builds the subscription pipeline.

Solutions

  1. Ensure the outer source is a valid IAsyncObservable<IAsyncObservable<TSource>> before calling Merge
  2. Check the preceding operator/factory in the chain for null returns
  3. If sources may be absent, default to AsyncObservable.Empty<IAsyncObservable<TSource>>()

Example fix

// before
IAsyncObservable<IAsyncObservable<int>> sources = GetSources(); // returns null
var merged = sources.Merge();
// after
var merged = (sources ?? AsyncObservable.Empty<IAsyncObservable<int>>()).Merge();
Defensive patterns

Strategy: validation

Validate before calling

if (sources == null) throw new InvalidOperationException("Outer source for Merge must not be null");

Type guard

bool HasSource<T>(IAsyncObservable<T> s) => s is not null;

Try / catch

try { var merged = sources.Merge(); } catch (ArgumentNullException ex) when (ex.ParamName == "source") { /* fall back to empty source */ }

Prevention

When it happens

Trigger: Calling nullSource.Merge<TSource>() — e.g. an IAsyncObservable<IAsyncObservable<T>> variable that is null because an upstream factory returned null.

Common situations: Chaining Merge after an operator that returned null; conditional pipeline construction leaving the source unassigned; deserialized/configured sources that failed to resolve.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Merge.cs:18

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 

using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        // TODO: Add Merge with max concurrency and IEnumerable<T>-based overloads.

        public static IAsyncObservable<TSource> Merge<TSource>(this IAsyncObservable<IAsyncObservable<TSource>> source)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            return Create<TSource>(async observer =>
            {
                var (sink, cancel) = AsyncObserver.Merge(observer);

                var subscription = await source.SubscribeSafeAsync(sink).ConfigureAwait(false);

                return StableCompositeAsyncDisposable.Create(subscription, cancel);
            });
        }
    }

    public partial class AsyncObserver
    {
        public static (IAsyncObserver<IAsyncObservable<TSource>>, IAsyncDisposable) Merge<TSource>(IAsyncObserver<TSource> observer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));

View on GitHub (pinned to 94b5d5ab91)