dotnet/reactive · error · ArgumentNullException

throw new ArgumentNullException(nameof(observer));

Error message

throw new ArgumentNullException(nameof(observer));

What it means

The AsyncObserver.Sample<TSource,TSample>(observer) sink factory throws ArgumentNullException when the observer is null. This lower-level API builds the paired (source observer, sampler observer) sink used by the operator, and it validates the downstream observer before allocating the gate and state.

Solutions

  1. Pass a real downstream IAsyncObserver (e.g. one obtained from AsyncObserver.Create or your sink chain)
  2. Guard in the calling operator: if (observer == null) throw new ArgumentNullException(nameof(observer)) before delegating
  3. Ensure the custom operator forwards its own observer parameter, not a field that may be null
  4. Subscribe via the public Sample operator instead of constructing the sink manually

Example fix

// before
var (src, smp) = AsyncObserver.Sample<Price, Tick>(downstream); // downstream may be null
// after
if (downstream == null) throw new ArgumentNullException(nameof(downstream));
var (src, smp) = AsyncObserver.Sample<Price, Tick>(downstream);
Defensive patterns

Strategy: validation

Validate before calling

if (observer is null) throw new ArgumentNullException(nameof(observer));
var (srcObs, smpObs) = AsyncObserver.Sample<TSource, TSample>(observer);

Type guard

static bool HasObserver<TSource>(IAsyncObserver<TSource>? o) => o is not null;

Try / catch

try
{
    var (srcObs, smpObs) = AsyncObserver.Sample<TSource, TSample>(observer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "observer")
{
    // downstream was not wired; fix subscription chain
    throw new InvalidOperationException("Sample sink requires a downstream observer", ex);
}

Prevention

When it happens

Trigger: Calling AsyncObserver.Sample<T,TSample>(null) directly — e.g. hand-rolling the Sample operator internals, or forwarding a null downstream observer from a custom operator pipeline.

Common situations: Custom operator authorship where the observer comes from an outer subscription that can be null; wiring observer pairs in test harnesses; refactoring that lost the downstream observer argument.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Sample.cs:79

                source,
                (scheduler, interval),
                static async (source, state, observer) =>
                {
                    var (sourceSink, sampler) = await AsyncObserver.Sample(observer, state.interval, state.scheduler).ConfigureAwait(false);

                    var sourceSubscription = await source.SubscribeSafeAsync(sourceSink).ConfigureAwait(false);

                    return StableCompositeAsyncDisposable.Create(sourceSubscription, sampler);
                });
        }
    }

    public partial class AsyncObserver
    {
        public static (IAsyncObserver<TSource>, IAsyncObserver<TSample>) Sample<TSource, TSample>(IAsyncObserver<TSource> observer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));

            var gate = new AsyncGate();

            var hasValue = false;
            var value = default(TSource);
            var atEnd = false;

            async ValueTask OnSampleAsync()
            {
                using (await gate.LockAsync().ConfigureAwait(false))
                {
                    if (hasValue)
                    {
                        hasValue = false;
                        await observer.OnNextAsync(value).ConfigureAwait(false);
                    }

                    if (atEnd)

View on GitHub (pinned to 94b5d5ab91)