dotnet/reactive · error · ArgumentNullException
exception
Error message
exception
What it means
SetException requires a non-null Exception; passing null throws ArgumentNullException. It also throws if the observable already completed (the SetResult path already ran), because a task-like observable can only complete once.
Solutions
- Pass the actual caught exception; never forward null (e.g. check Task.IsFaulted before reading Exception).
- Ensure SetException is invoked exactly once, guarded by completion flags or try/finally semantics.
- Guard completion with an Interlocked.CompareExchange flag in bridging code.
Example fix
// before builder.SetException(task.Exception); // may be null on success // after if (task.IsFaulted && task.Exception != null) builder.SetException(task.Exception);
Defensive patterns
Strategy: validation
Validate before calling
if (exception == null) throw new ArgumentNullException(nameof(exception));
if (completed) throw new InvalidOperationException("observable already completed"); Prevention
- Only forward exceptions from faulted tasks (check IsFaulted first).
- Track completion with a flag so SetException/SetResult are mutually exclusive and called once.
When it happens
Trigger: Calling SetException(null), or calling SetException after SetResult/SetException/OnCompleted already completed the observable.
Common situations: Bridging code that forwards exceptions from a task whose Exception property is null on success; double-completion race in custom awaitable observables.
Related errors
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/e7ef8a884c4a57a9.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Runtime/CompilerServices/TaskObservableMethodBuilder.cs:103
_inner = new TaskObservable(result);
}
else
{
_inner.SetResult(result);
}
}
/// <summary>
/// Marks the observable as failed and binds the specified exception to the observable sequence.
/// </summary>
/// <param name="exception">The exception to bind to the observable sequence.</param>
/// <exception cref="ArgumentNullException"><paramref name="exception"/> is <c>null</c>.</exception>
/// <exception cref="InvalidOperationException">The observable has already completed.</exception>
public void SetException(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
if (_inner == null)
{
_inner = new TaskObservable(exception);
}
else
{
_inner.SetException(exception);
}
}
/// <summary>
/// Gets the observable sequence for this builder.
/// </summary>
public ITaskObservable<T> Task => _inner ??= new TaskObservable();
/// <summary>View on GitHub (pinned to 94b5d5ab91)