dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'error')

Error message

Value cannot be null. (Parameter 'error')

What it means

UnsafeAsyncObserver.OnErrorAsync guards its error parameter and throws ArgumentNullException when the Exception argument is null. Passing null to signal an error is not allowed; observers must always receive a concrete Exception instance describing the failure. This is a per-call guard on the delivery path, unlike the constructor guards at lines 15-17.

Solutions

  1. Always pass a real Exception, e.g. new InvalidOperationException("unknown failure") when no better one exists
  2. Audit code that forwards exceptions to OnErrorAsync and ensure the variable cannot be null at the call site
  3. Wrap the forwarding call in a null check or use ex ?? new Exception(...) before calling

Example fix

// before
await observer.OnErrorAsync(lastException); // lastException may be null
// after
await observer.OnErrorAsync(lastException ?? new InvalidOperationException("An error occurred with no exception details."));
Defensive patterns

Strategy: validation

Validate before calling

if (error == null) error = new InvalidOperationException("Unspecified error");

Type guard

static bool HasException(Exception ex) => ex != null;

Try / catch

try { await observer.OnErrorAsync(ex); } catch (ArgumentNullException) { await observer.OnErrorAsync(new InvalidOperationException("null error supplied")); }

Prevention

When it happens

Trigger: Calling observer.OnErrorAsync(null) — e.g. forwarding an exception variable that was null, or a pipeline that synthesizes error notifications without an Exception instance.

Common situations: Bridging from another reactive/async library where errors can be signaled without an exception object; code paths where a caught exception was reassigned to null; logging middleware that nulls out the exception before forwarding.

Related errors


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

Appendix: source

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

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)