dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'onErrorAsync')

Error message

Value cannot be null. (Parameter 'onErrorAsync')

What it means

The AsyncObserver constructor null-checks all three delegates and throws ArgumentNullException naming the first null one; here onErrorAsync was null. Without an error handler the observer could not propagate faults from the async sequence, so construction is rejected eagerly (line 18).

Solutions

  1. Provide a non-null error handler, e.g. async ex => await LogErrorAsync(ex) or ex => { ...; return default; }.
  2. Use AsyncObserver.Create or SubscribeAsync helpers that let you compose observers without constructing AsyncObserver directly.
  3. If errors should be swallowed, pass ex => default (a no-op) rather than null.
  4. Note the same constructor also guards OnErrorAsyncCore's error parameter — never invoke the observer with a null Exception.

Example fix

// before
var observer = new AsyncObserver<int>(x => default, null, () => default);
// after
var observer = new AsyncObserver<int>(x => default, ex => { Console.Error.WriteLine(ex); return default; }, () => default);
Defensive patterns

Strategy: validation

Validate before calling

if (onNextAsync == null || onErrorAsync == null || onCompletedAsync == null)
    throw new ArgumentException("AsyncObserver requires non-null onNextAsync, onErrorAsync, and onCompletedAsync delegates.");
var observer = new AsyncObserver<T>(onNextAsync, onErrorAsync, onCompletedAsync);

Try / catch

try
{
    var observer = new AsyncObserver<T>(onNextAsync, onErrorAsync, onCompletedAsync);
}
catch (ArgumentNullException ex) when (ex.ParamName == "onErrorAsync")
{
    observer = new AsyncObserver<T>(onNextAsync, ex2 => default, onCompletedAsync);
}

Prevention

When it happens

Trigger: new AsyncObserver<T>(onNextAsync, null, onCompletedAsync) — constructing with a null error-handler delegate.

Common situations: Hand-building observers in tests where error handling was deemed unnecessary; wrapper libraries forwarding an optional callback straight through as null; code migrated from sync Rx where null handlers were tolerated by some implementations.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/AsyncObserver.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.Threading.Tasks;

namespace System.Reactive
{
    public class AsyncObserver<T> : AsyncObserverBase<T>
    {
        private readonly Func<T, ValueTask> _onNextAsync;
        private readonly Func<Exception, ValueTask> _onErrorAsync;
        private readonly Func<ValueTask> _onCompletedAsync;

        public AsyncObserver(Func<T, ValueTask> onNextAsync, Func<Exception, ValueTask> onErrorAsync, Func<ValueTask> onCompletedAsync)
        {
            _onNextAsync = onNextAsync ?? throw new ArgumentNullException(nameof(onNextAsync));
            _onErrorAsync = onErrorAsync ?? throw new ArgumentNullException(nameof(onErrorAsync));
            _onCompletedAsync = onCompletedAsync ?? throw new ArgumentNullException(nameof(onCompletedAsync));
        }

        protected override ValueTask OnCompletedAsyncCore() => _onCompletedAsync();

        protected override ValueTask OnErrorAsyncCore(Exception error) => _onErrorAsync(error ?? throw new ArgumentNullException(nameof(error)));

        protected override ValueTask OnNextAsyncCore(T value) => _onNextAsync(value);
    }
}

View on GitHub (pinned to 94b5d5ab91)