dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'observable')
Error message
Value cannot be null. (Parameter 'observable')
What it means
ToTask<TResult> throws ArgumentNullException with parameter name 'observable' when the source IObservable is null. The extension method validates its receiver before creating the TaskCompletionSource and observer.
Solutions
- Ensure the observable is initialized before calling ToTask.
- Null-check or use the null-conditional operator (?.) with a fallback.
- Fix upstream factory/DI code that returns null instead of an empty observable (Observable.Empty<TResult>()).
Example fix
// before var task = maybeObservable.ToTask(); // after var task = (maybeObservable ?? Observable.Empty<TResult>()).ToTask();
Defensive patterns
Strategy: type-guard
Validate before calling
if (observable == null) return Task.FromResult(default(TResult)!);
Type guard
bool IsValidSource<TResult>(IObservable<TResult>? o) => o is not null;
// usage: if (IsValidSource(source)) { var t = source.ToTask(); } Try / catch
try { task = source.ToTask(); }
catch (ArgumentNullException) { task = Task.FromCanceled<TResult>(CancellationToken.None); } Prevention
- Initialize observable fields with Observable.Empty<T>() instead of null.
- Ensure factories/DI never return null observables.
When it happens
Trigger: Invoking source.ToTask() where the source reference is null (extension methods can be called on null receivers).
Common situations: Nullable fields/properties holding the observable that were never initialized; factory methods returning null; chaining pipelines where an earlier operator produced null.
Related errors
- action
- action (Parameter 'action')
- action (Parameter 'action')
- action (Value cannot be null)
- ArgumentNullException(nameof(first))
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/5f19216245c23962.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Threading/Tasks/TaskObservableExtensions.cs:591
}
}
/// <summary>
/// Returns a task that will receive the last value or the exception produced by the observable sequence.
/// </summary>
/// <typeparam name="TResult">The type of the elements in the source sequence.</typeparam>
/// <param name="observable">Observable sequence to convert to a task.</param>
/// <param name="cancellationToken">Cancellation token that can be used to cancel the task, causing unsubscription from the observable sequence.</param>
/// <param name="state">The state to use as the underlying task's <see cref="Task.AsyncState"/>.</param>
/// <returns>A task that will receive the last element or the exception produced by the observable sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="observable"/> is <c>null</c>.</exception>
#pragma warning disable CA1068 // (CancellationToken parameters must come last.) Would be a breaking change.
public static Task<TResult> ToTask<TResult>(this IObservable<TResult> observable, CancellationToken cancellationToken, object? state)
#pragma warning restore CA1068
{
if (observable == null)
{
throw new ArgumentNullException(nameof(observable));
}
var tcs = new TaskCompletionSource<TResult>(state);
var taskCompletionObserver = new ToTaskObserver<TResult>(tcs, cancellationToken);
//
// Subtle race condition: if the source completes before we reach the line below, the SingleAssigmentDisposable
// will already have been disposed. Upon assignment, the disposable resource being set will be disposed on the
// spot, which may throw an exception.
//
try
{
//
// [OK] Use of unsafe Subscribe: we're catching the exception here to set the TaskCompletionSource.
//
// Notice we could use a safe subscription to route errors through OnError, but we still need the
// exception handling logic here for the reason explained above. We cannot afford to throw hereView on GitHub (pinned to 94b5d5ab91)