dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'subscription')

Error message

Value cannot be null. (Parameter 'subscription')

What it means

This ArgumentNullException is thrown by the count-based Window helper when the subscription IAsyncDisposable is null. The subscription represents the upstream disposable and is wrapped in a RefCountAsyncDisposable, so a null value cannot be accepted. It fails fast at the call, before any windows are created.

Solutions

  1. Create and pass a real disposable, e.g. new CompositeAsyncDisposable(), before calling Window.
  2. Pass the disposable you obtained from the upstream SubscribeAsync call.
  3. Use AsyncDisposable.Nop / an empty composite if no upstream subscription exists yet.
  4. Add a null check in your wrapper so the error points at your call site.

Example fix

// before
var (winObs, disp) = WindowOperators.Window<TSource>(observer, null, 5);
// after
var d = new CompositeAsyncDisposable();
var (winObs, disp) = WindowOperators.Window<TSource>(observer, d, 5);
Defensive patterns

Strategy: validation

Validate before calling

if (subscription is null) throw new ArgumentNullException(nameof(subscription));
// or: subscription ??= AsyncDisposable.Nop;

Type guard

bool IsValidDisposable(IAsyncDisposable? d) => d is not null;

Try / catch

try
{
    var (winObs, disp) = WindowOperators.Window<TSource>(observer, subscription, count);
}
catch (ArgumentNullException ex) when (ex.ParamName == "subscription")
{
    subscription = new CompositeAsyncDisposable();
}

Prevention

When it happens

Trigger: Calling Window(observer, null, count) or Window(observer, null, count, skip) — usually when wiring a custom operator and the upstream disposable hasn't been created yet.

Common situations: Custom operator implementations where the CompositeAsyncDisposable is created after the helper call; test harnesses passing null disposables; reordering refactors in operator plumbing.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Window.cs:223

            var (sink, subscription) = await createObserverAsync(observer, d).ConfigureAwait(false);

            var inner = await source.SubscribeSafeAsync(sink).ConfigureAwait(false);
            await d.AssignAsync(inner).ConfigureAwait(false);

            return subscription;
        }
    }

    public partial class AsyncObserver
    {
        public static (IAsyncObserver<TSource>, IAsyncDisposable) Window<TSource>(IAsyncObserver<IAsyncObservable<TSource>> observer, IAsyncDisposable subscription, int count) => Window(observer, subscription, count, count);

        public static (IAsyncObserver<TSource>, IAsyncDisposable) Window<TSource>(IAsyncObserver<IAsyncObservable<TSource>> observer, IAsyncDisposable subscription, int count, int skip)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (subscription == null)
                throw new ArgumentNullException(nameof(subscription));
            if (count <= 0)
                throw new ArgumentOutOfRangeException(nameof(count));
            if (skip <= 0)
                throw new ArgumentOutOfRangeException(nameof(skip));

            var refCount = new RefCountAsyncDisposable(subscription);

            var queue = new Queue<IAsyncSubject<TSource>>();
            var n = 0;

            return
                (
                    Create<TSource>
                    (
                        async x =>
                        {
                            foreach (var window in queue)
                            {

View on GitHub (pinned to 94b5d5ab91)