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
- Pass a real downstream IAsyncObserver (e.g. one obtained from AsyncObserver.Create or your sink chain)
- Guard in the calling operator: if (observer == null) throw new ArgumentNullException(nameof(observer)) before delegating
- Ensure the custom operator forwards its own observer parameter, not a field that may be null
- 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
- Forward the observer parameter, never a possibly-null field, when writing custom operators
- Null-check downstream observers at operator entry
- Prefer the public AsyncObservable.Sample operator over raw sink APIs
- Initialize observer fields in constructors, not lazily
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
- Value cannot be null. (Parameter 'observer')
- Argument cannot be null (Parameter name: observer)
- throw new ArgumentNullException(nameof(source));
- throw new ArgumentNullException(nameof(sampler));
- throw new ArgumentNullException(nameof(scheduler));
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)