dotnet/reactive · error · ArgumentNullException

observableFactory

Error message

observableFactory

What it means

The ValueTask-based overload Defer<TSource>(Func<ValueTask<IAsyncObservable<TSource>>>) (also reached via DeferAsync) throws ArgumentNullException naming 'observableFactory' when the async factory delegate is null. The factory is invoked inside Create at subscription time, so a null must be rejected up front.

Solutions

  1. Provide a non-null async factory: () => new ValueTask<IAsyncObservable<T>>(source).
  2. Verify you called the intended overload; use DeferAsync for ValueTask-returning factories.
  3. Null-check the factory delegate before passing it into Defer/DeferAsync.

Example fix

// before
Func<ValueTask<IAsyncObservable<int>>> factory = maybeNull;
var xs = AsyncObservable.DeferAsync(factory);
// after
var xs = AsyncObservable.DeferAsync(factory ?? throw new ArgumentNullException(nameof(factory)));
Defensive patterns

Strategy: validation

Validate before calling

if (observableFactory is null) throw new ArgumentNullException(nameof(observableFactory));
var xs = AsyncObservable.DeferAsync(observableFactory);

Type guard

static bool IsValidAsyncFactory<T>(Func<ValueTask<IAsyncObservable<T>>> f) => f is not null;

Try / catch

try
{
    var xs = AsyncObservable.DeferAsync(factory);
}
catch (ArgumentNullException ex) when (ex.ParamName == "observableFactory")
{
    // async ValueTask factory was null
}

Prevention

When it happens

Trigger: Calling AsyncObservable.Defer<TSource>((Func<ValueTask<IAsyncObservable<TSource>>>)null) or DeferAsync<TSource>(null); also calling Defer with a sync Func that the compiler resolved to the ValueTask overload as null.

Common situations: Async factory supplied by a nullable delegate field or DI-resolved function that was never assigned; overload-resolution confusion between the sync, ValueTask, and CancellationToken ValueTask variants of Defer.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Defer.cs:26

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<TSource> Defer<TSource>(Func<IAsyncObservable<TSource>> observableFactory)
        {
            if (observableFactory == null)
                throw new ArgumentNullException(nameof(observableFactory));

            return Defer(() => new ValueTask<IAsyncObservable<TSource>>(observableFactory()));
        }

        public static IAsyncObservable<TSource> DeferAsync<TSource>(Func<ValueTask<IAsyncObservable<TSource>>> observableFactory) => Defer(observableFactory);

        public static IAsyncObservable<TSource> Defer<TSource>(Func<ValueTask<IAsyncObservable<TSource>>> observableFactory)
        {
            if (observableFactory == null)
                throw new ArgumentNullException(nameof(observableFactory));

            return Create<TSource>(async observer =>
            {
                var source = default(IAsyncObservable<TSource>);

                try
                {
                    source = await observableFactory().ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    await observer.OnErrorAsync(ex).ConfigureAwait(false);
                    return AsyncDisposable.Nop;
                }

                return await source.SubscribeSafeAsync(observer).ConfigureAwait(false);
            });
        }

View on GitHub (pinned to 94b5d5ab91)