dotnet/reactive · error · ArgumentNullException

ArgumentNullException: observer

Error message

ArgumentNullException: observer

What it means

The full-overload GroupByUntil(observer, subscription, keySelector, durationSelector, capacity, comparer) (GroupByUntil.cs:365) first validates its observer argument — the IAsyncObserver that receives the grouped observables — and throws ArgumentNullException when it is null. Without an observer there is nowhere to deliver group results, so the call is rejected up front.

Solutions

  1. Supply a valid IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> to the operator
  2. Check where the observer comes from — ensure the subscription/pipeline was fully initialized before calling GroupByUntil
  3. If writing a custom operator, throw or defer subscription when the downstream observer is null instead of forwarding it

Example fix

// before
await AsyncObservable.GroupByUntil(null, subscription, k => k, g => g.Take(1), 8, cmp);
// after
if (observer == null) throw new InvalidOperationException("observer not initialized");
await AsyncObservable.GroupByUntil(observer, subscription, k => k, g => g.Take(1), 8, cmp);
Defensive patterns

Strategy: validation

Validate before calling

if (observer == null) throw new InvalidOperationException("Downstream observer must be initialized before applying GroupByUntil");

Type guard

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

Try / catch

try { var res = AsyncObservable.GroupByUntil(observer, subscription, keySelector, durationSelector, capacity, comparer); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* re-create or await the pipeline observer */ }

Prevention

When it happens

Trigger: Invoking the internal/operator surface of GroupByUntil with observer == null; in practice this happens in custom pipeline code or operator composition where the downstream observer is a nullable field or the result of a failed lookup.

Common situations: Writing custom operators that forward a downstream observer which can be null before subscription completes; unit tests constructing the operator manually; DI containers producing null observer instances.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs:368

        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (subscription == null)
                throw new ArgumentNullException(nameof(subscription));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));

            return GroupByUntil(observer, subscription, keySelector, durationSelector, capacity, EqualityComparer<TKey>.Default);
        }

        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TSource>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity, IEqualityComparer<TKey> comparer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (subscription == null)
                throw new ArgumentNullException(nameof(subscription));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (durationSelector == null)
                throw new ArgumentNullException(nameof(durationSelector));
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return GroupByUntil(observer, subscription, x => new ValueTask<TKey>(keySelector(x)), durationSelector, capacity, comparer);
        }

        public static ValueTask<(IAsyncObserver<TSource>, IAsyncDisposable)> GroupByUntil<TSource, TKey, TElement, TDuration>(IAsyncObserver<IGroupedAsyncObservable<TKey, TElement>> observer, IAsyncDisposable subscription, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<IGroupedAsyncObservable<TKey, TElement>, IAsyncObservable<TDuration>> durationSelector)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));

View on GitHub (pinned to 94b5d5ab91)