dotnet/reactive · error · ArgumentNullException

Argument cannot be null (Parameter name: source)

Error message

Argument cannot be null (Parameter name: source)

What it means

ForEachAsync(IAsyncObservable<TSource>, Action<TSource>, CancellationToken) throws ArgumentNullException because the source observable is null. ForEachAsync is an extension method, but extension methods can be invoked with a null receiver (including explicit static calls), so the null check names 'source'. Validation happens immediately, before the subscription work begins.

Solutions

  1. Ensure the source observable is created (e.g. AsyncObservable.Return/FromEvent/Empty) before iterating it.
  2. Guard: if (source == null) return Task.CompletedTask; when absence legitimately means 'nothing to observe'.
  3. Fix the producer that returns null so it returns AsyncObservable.Empty<TSource>() instead.
  4. If using an explicit static call, pass a real IAsyncObservable<TSource> instance.

Example fix

// before
var source = _registry.TryGet(key); // returns null when missing
await source.ForEachAsync(x => Handle(x));

// after
var source = _registry.TryGet(key);
if (source != null)
    await source.ForEachAsync(x => Handle(x));
Defensive patterns

Strategy: validation

Validate before calling

if (source == null)
    return Task.CompletedTask; // nothing to observe
return source.ForEachAsync(onNext, token);

Type guard

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

Try / catch

try { await source.ForEachAsync(onNext, token); }
catch (ArgumentNullException ex) when (ex.ParamName == "source") { /* source factory returned null — treat as no data or rethrow with context */ }

Prevention

When it happens

Trigger: Calling observable.ForEachAsync(onNext) where observable is null — e.g. a method that returns IAsyncObservable<T> and returned null, or an explicit AsyncObservable.ForEachAsync(null, action) call.

Common situations: Factory/DI methods that return null instead of an empty observable; caching a subscription source in a field that was never initialized; optional sources in configuration that were not created.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/ForEachAsync.cs:16

// 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
    {
        public static Task ForEachAsync<TSource>(this IAsyncObservable<TSource> source, Action<TSource> onNext, CancellationToken token = default)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (onNext == null)
                throw new ArgumentNullException(nameof(onNext));

            return ForEachAsyncCore(source, (x, i) => { onNext(x); return Task.CompletedTask; }, token);
        }

        public static Task ForEachAsync<TSource>(this IAsyncObservable<TSource> source, Func<TSource, Task> onNext, CancellationToken token = default)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (onNext == null)
                throw new ArgumentNullException(nameof(onNext));

            return ForEachAsyncCore(source, (x, i) => onNext(x), token);
        }

        public static Task ForEachAsync<TSource>(this IAsyncObservable<TSource> source, Action<TSource, int> onNext, CancellationToken token = default)
        {

View on GitHub (pinned to 94b5d5ab91)