dotnet/reactive · error · ArgumentNullException
nameof(onError)
Error message
nameof(onError)
What it means
System.Reactive.Async's Do operator requires a non-null onError callback so that error notifications from the source sequence can be forwarded to the witness observer. Passing null makes it impossible to build the observer delegate, so the library throws ArgumentNullException immediately, before any subscription happens. This is a fail-fast programming-contract check, not a runtime data error.
Solutions
- Pass a non-null onError handler; use a no-op such as ex => { } (or ex => Task.CompletedTask for the async overload) if you do not care about errors.
- Log the error in onError rather than dropping it, so silent failure is avoided.
- Add a null check or default argument at the call site before invoking Do.
Example fix
// before var observer = Do(src, OnNextAsync, null, OnCompletedAsync); // after var observer = Do(src, OnNextAsync, ex => Console.WriteLine(ex), OnCompletedAsync);
Defensive patterns
Strategy: validation
Validate before calling
if (onError == null) onError = ex => { }; // or assert: ArgumentNullException.ThrowIfNull(onError) at your own boundary
var observed = Do(observer, onNext, onError, onCompleted); Type guard
bool IsValidHandler(Delegate d) => d is not null;
Try / catch
try
{
var observed = Do(observer, onNext, onError, onCompleted);
}
catch (ArgumentNullException ex) when (ex.ParamName == "onError")
{
logger.LogWarning("Do() called without an error handler; using no-op");
var observed = Do(observer, onNext, ex2 => { }, onCompleted);
} Prevention
- Never pass null for observer callbacks; prefer explicit no-op lambdas so intent is visible.
- Wrap operator composition in a helper that fills in default handlers.
- Enable nullable reference types so null delegate flow is caught at compile time.
When it happens
Trigger: Calling the Do<TSource>(IAsyncObserver<TSource> observer, Func<TSource,Task> onNext, Func<Exception,Task> onError, Func<Task> onCompleted) overload (Do.cs:242) with a null onError argument while the other three arguments are non-null.
Common situations: Developers build handler lambdas conditionally (e.g. only wiring onNext and onCompleted) and pass a null onError; refactoring from the simpler Do(observer, onNext) overload to the full triple-callback overload and forgetting to supply the error handler; interop code that maps an external observer whose error handler is null.
Related errors
- nameof(onCompleted)
- nameof(witness)
- threadFactory (Parameter 'threadFactory')
- action (Parameter 'action')
- comparer (Parameter 'comparer')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/f74310754a6f275a.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Do.cs:242
public static IAsyncObserver<TSource> Do<TSource>(IAsyncObserver<TSource> observer, Func<ValueTask> onCompleted)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer));
if (onCompleted == null)
throw new ArgumentNullException(nameof(onCompleted));
return Do(observer, Create<TSource>(_ => default, _ => default, onCompleted));
}
public static IAsyncObserver<TSource> Do<TSource>(IAsyncObserver<TSource> observer, Func<TSource, ValueTask> onNext, Func<Exception, ValueTask> onError, Func<ValueTask> onCompleted)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer));
if (onNext == null)
throw new ArgumentNullException(nameof(onNext));
if (onError == null)
throw new ArgumentNullException(nameof(onError));
if (onCompleted == null)
throw new ArgumentNullException(nameof(onCompleted));
return Do(observer, Create(onNext, onError, onCompleted));
}
public static IAsyncObserver<TSource> Do<TSource>(IAsyncObserver<TSource> observer, IObserver<TSource> witness)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer));
if (witness == null)
throw new ArgumentNullException(nameof(witness));
return Create<TSource>(
async x =>
{
try
{View on GitHub (pinned to 94b5d5ab91)