dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'onNextAsync')

Error message

Value cannot be null. (Parameter 'onNextAsync')

What it means

UnsafeAsyncObserver wraps three delegates (onNextAsync, onErrorAsync, onCompletedAsync) that its On*Async methods invoke directly; null delegates would crash mid-stream. The constructor therefore throws ArgumentNullException for the first, onNextAsync, when null.

Solutions

  1. Pass a non-null Func<T, ValueTask> as onNextAsync (use _ => default(ValueTask) for a no-op).
  2. Check ordering of arguments — onNext comes first, before onErrorAsync and onCompletedAsync.
  3. Fix the producer/factory returning the null delegate.

Example fix

// before
var observer = new UnsafeAsyncObserver<int>(maybeNext, OnError, OnCompleted);
// after
var observer = new UnsafeAsyncObserver<int>(
    maybeNext ?? (x => default), OnError, OnCompleted);
Defensive patterns

Strategy: validation

Validate before calling

if (onNextAsync is null) throw new ArgumentNullException(nameof(onNextAsync));
var observer = new UnsafeAsyncObserver<T>(onNextAsync, onErrorAsync, onCompletedAsync);

Type guard

static bool HasOnNext<T>(Func<T, ValueTask>? f) => f is not null;

Try / catch

try { var obs = new UnsafeAsyncObserver<int>(onNext, onError, onCompleted); }
catch (ArgumentNullException ex) when (ex.ParamName == "onNextAsync")
{ log.LogError("onNextAsync delegate was null"); }

Prevention

When it happens

Trigger: Calling new UnsafeAsyncObserver<T>(null, onErrorAsync, onCompletedAsync) — e.g. building an observer programmatically where the next-handler is conditionally constructed or comes from a null-returning factory.

Common situations: Constructing observers from configuration/delegates loaded from DI where one binding is missing; generic helper methods forwarding possibly-null lambdas; typos passing arguments in the wrong order.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Internal/UnsafeAsyncObserver.cs:17

// 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 UnsafeAsyncObserver<T> : IAsyncObserver<T>
    {
        private readonly Func<T, ValueTask> _onNextAsync;
        private readonly Func<Exception, ValueTask> _onErrorAsync;
        private readonly Func<ValueTask> _onCompletedAsync;

        public UnsafeAsyncObserver(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));
        }

        public ValueTask OnCompletedAsync() => _onCompletedAsync();

        public ValueTask OnErrorAsync(Exception error) => _onErrorAsync(error ?? throw new ArgumentNullException(nameof(error)));

        public ValueTask OnNextAsync(T value) => _onNextAsync(value);
    }
}

View on GitHub (pinned to 94b5d5ab91)