dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'source')

Error message

Value cannot be null. (Parameter 'source')

What it means

The Catch operator subscribes to a source and, when it fails with TException, switches to the observable returned by handler; a null source fails the guard and throws ArgumentNullException. Validation happens when the operator is constructed, not at subscription time.

Solutions

  1. Ensure the source IAsyncObservable<TSource> is non-null before calling Catch
  2. Substitute AsyncObservable.Empty<TSource>(scheduler) for a legitimately absent source
  3. Fix the upstream factory returning null

Example fix

// before
var result = AsyncObservable.Catch<int, TimeoutException>(null, ex => fallback);
// after
var result = AsyncObservable.Catch<int, TimeoutException>(primary ?? AsyncObservable.Empty<int>(Scheduler.Default), ex => fallback);
Defensive patterns

Strategy: validation

Validate before calling

if (source == null) throw new InvalidOperationException("Catch requires a non-null source observable");

Type guard

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

Try / catch

try { var result = source.Catch<TimeoutException>(handler); }
catch (ArgumentNullException ex) when (ex.ParamName == "source") { /* use fallback stream directly */ }

Prevention

When it happens

Trigger: Calling source.Catch<TException>(handler) where source is null, e.g. Catch(null, ex => fallback) via static syntax.

Common situations: Pipelines where a preceding operator or factory returned null; composing Catch over an optional stream that was not instantiated.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Catch.cs:19

// 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.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading.Tasks;

namespace System.Reactive.Linq
{
    // TODO: Implement tail call behavior to flatten Catch chains.

    public partial class AsyncObservable
    {
        public static IAsyncObservable<TSource> Catch<TSource, TException>(this IAsyncObservable<TSource> source, Func<TException, IAsyncObservable<TSource>> handler)
            where TException : Exception
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));

            return Create(
                source,
                handler,
                static async (source, handler, observer) =>
                {
                    var (sink, inner) = AsyncObserver.Catch(observer, handler);

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

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

        public static IAsyncObservable<TSource> Catch<TSource, TException>(this IAsyncObservable<TSource> source, Func<TException, ValueTask<IAsyncObservable<TSource>>> handler)
            where TException : Exception

View on GitHub (pinned to 94b5d5ab91)