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

  1. Pass the actual caught exception; never forward null (e.g. check Task.IsFaulted before reading Exception).
  2. Ensure SetException is invoked exactly once, guarded by completion flags or try/finally semantics.
  3. 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

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)