dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'observer')

Error message

Value cannot be null. (Parameter 'observer')

What it means

AsyncObserver.Range(observer, start, count) (and its scheduler overload) validates that the target IAsyncObserver<int> is non-null before scheduling value emission. A null observer throws ArgumentNullException naming 'observer'. The observer is the sink that receives the generated values, so the operation cannot proceed without it.

Solutions

  1. Pass a valid IAsyncObserver<int> implementation (e.g. from CreateSubscribeAsync or an AsyncObserver helper).
  2. If you only need the sequence, use AsyncObservable.Range(start, count) which wires the observer for you.
  3. Check the code path that produced the observer reference; fix why it is null.
  4. Add a non-null assertion/guard in your operator before delegating to AsyncObserver.Range.

Example fix

// before
await AsyncObserver.Range(observer, 0, 10); // observer is null
// after
if (observer == null) throw new InvalidOperationException("observer not initialized");
await AsyncObserver.Range(observer, 0, 10);
Defensive patterns

Strategy: validation

Validate before calling

if (observer == null) throw new ArgumentNullException(nameof(observer));

Type guard

static bool IsValidObserver(IAsyncObserver<int> observer) => observer is not null;

Try / catch

try { await AsyncObserver.Range(observer, start, count); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* initialize or substitute observer */ }

Prevention

When it happens

Trigger: Calling AsyncObserver.Range(null, 0, 10) directly; passing an observer variable that was never assigned, or the result of a factory/pipe that returned null; also triggered via the 4-argument overload with a null observer (the check runs before count/scheduler checks).

Common situations: Writing custom async observable operators that forward an observer parameter which is null; misusing the low-level AsyncObserver API instead of AsyncObservable.Range; test code constructing observers lazily and passing null on a failure path.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Range.cs:38

        public static IAsyncObservable<int> Range(int start, int count, IAsyncScheduler scheduler)
        {
            if (count < 0 || ((long)start) + count - 1 > int.MaxValue)
                throw new ArgumentOutOfRangeException(nameof(count));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return Create<int>(observer => AsyncObserver.Range(observer, start, count, scheduler));
        }
    }

    public partial class AsyncObserver
    {
        public static ValueTask<IAsyncDisposable> Range(IAsyncObserver<int> observer, int start, int count) => Range(observer, start, count, TaskPoolAsyncScheduler.Default);

        public static ValueTask<IAsyncDisposable> Range(IAsyncObserver<int> observer, int start, int count, IAsyncScheduler scheduler)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (count < 0 || ((long)start) + count - 1 > int.MaxValue)
                throw new ArgumentOutOfRangeException(nameof(count));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return scheduler.ScheduleAsync(async ct =>
            {
                if (ct.IsCancellationRequested)
                    return;

                for (int i = start, end = start + count - 1; i <= end && !ct.IsCancellationRequested; i++)
                {
                    await observer.OnNextAsync(i).RendezVous(scheduler, ct);
                }

                if (ct.IsCancellationRequested)
                    return;

View on GitHub (pinned to 94b5d5ab91)