dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'handlers')

Error message

Value cannot be null. (Parameter 'handlers')

What it means

AsyncObserver.Catch<TSource>(observer, IEnumerator<IAsyncObservable<TSource>> handlers) requires an enumerator of fallback observables to try in sequence when errors occur. A null enumerator leaves no fallback sequence, so ArgumentNullException('handlers') is thrown after the observer check.

Solutions

  1. Ensure the source collection is non-null before calling GetEnumerator()
  2. Coalesce: (handlers ?? Enumerable.Empty<IAsyncObservable<int>>()).GetEnumerator()
  3. Fix the config/loader that produced the null handler list

Example fix

// before
var (sink, disp) = AsyncObserver.Catch<int>(observer, configuredHandlers.GetEnumerator()); // configuredHandlers null
// after
var handlers = configuredHandlers ?? Enumerable.Empty<IAsyncObservable<int>>();
var (sink, disp) = AsyncObserver.Catch<int>(observer, handlers.GetEnumerator());
Defensive patterns

Strategy: validation

Validate before calling

if (observer is null) throw new ArgumentNullException(nameof(observer));
if (handlers is null) throw new ArgumentNullException(nameof(handlers));

Type guard

bool HasHandlerList<T>(IEnumerator<IAsyncObservable<T>> handlers) => handlers != null;

Try / catch

try { var (sink, d) = AsyncObserver.Catch<T>(observer, handlers); } catch (ArgumentNullException ex) when (ex.ParamName == "handlers") { /* enumerate a non-null collection */ }

Prevention

When it happens

Trigger: Passing null instead of an IEnumerator — e.g. calling handlers.GetEnumerator() on a null collection, or forwarding a nullable enumerator parameter from a caller.

Common situations: Retry lists read from configuration that can be empty/null, so GetEnumerator() is invoked on a null sequence.

Related errors


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

Appendix: source

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

                observer.OnNextAsync,
                async ex =>
                {
                    var secondSubscription = await second.SubscribeSafeAsync(observer).ConfigureAwait(false);

                    await subscription.AssignAsync(secondSubscription).ConfigureAwait(false);
                },
                observer.OnCompletedAsync
            );

            return (sink, subscription);
        }

        public static (IAsyncObserver<TSource>, IAsyncDisposable) Catch<TSource>(IAsyncObserver<TSource> observer, IEnumerator<IAsyncObservable<TSource>> handlers)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (handlers == null)
                throw new ArgumentNullException(nameof(handlers));

            var innerSubscription = new SerialAsyncDisposable();

            IAsyncObserver<TSource> GetSink() =>
                Create<TSource>(
                    observer.OnNextAsync,
                    async ex =>
                    {
                        var handler = default(IAsyncObservable<TSource>);

                        try
                        {
                            if (handlers.MoveNext())
                            {
                                handler = handlers.Current;
                            }
                        }
                        catch (Exception err)

View on GitHub (pinned to 94b5d5ab91)