dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'disposable')

Error message

Value cannot be null. (Parameter 'disposable')

What it means

The RefCountAsyncDisposable constructor throws ArgumentNullException when the underlying IAsyncDisposable is null. Reference-counting wrappers exist solely to share and ref-count a real disposable, so constructing one without a target is a programming error the library rejects eagerly.

Solutions

  1. Ensure the underlying disposable is created/assigned before constructing RefCountAsyncDisposable
  2. Add a null guard at the call site and fail fast with a clearer message
  3. If the disposable is conditionally available, defer creating the ref-count wrapper until it exists

Example fix

// before
var refCounted = new RefCountAsyncDisposable(_underlying); // _underlying is null
// after
if (_underlying == null)
    throw new InvalidOperationException("Underlying disposable was not initialized.");
var refCounted = new RefCountAsyncDisposable(_underlying);
Defensive patterns

Strategy: validation

Validate before calling

if (underlying is null)
    throw new InvalidOperationException("Underlying disposable must be created before ref-counting it.");
var refCounted = new RefCountAsyncDisposable(underlying);

Type guard

bool HasDisposable([NotNullWhen(true)] IAsyncDisposable? d) => d is not null;

Try / catch

try
{
    var refCounted = new RefCountAsyncDisposable(underlying);
}
catch (ArgumentNullException ex) when (ex.ParamName == "disposable")
{
    // underlying resource was never created; handle initialization failure here
}

Prevention

When it happens

Trigger: Passing a null IAsyncDisposable to 'new RefCountAsyncDisposable(...)' — typically when the base disposable came from an uninitialized field, a failed factory, or a nullable variable that was never assigned.

Common situations: Refactoring a nullable disposable into a non-null parameter; passing the result of an expression that returned null on a failure path; unit tests constructing the wrapper before the underlying resource exists.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Disposables/RefCountAsyncDisposable.cs:19

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 

using System.Threading;
using System.Threading.Tasks;

namespace System.Reactive.Disposables
{
    public sealed class RefCountAsyncDisposable : IAsyncDisposable
    {
        private readonly AsyncGate _gate = new();
        private IAsyncDisposable _disposable;
        private bool _primaryDisposed;
        private int _count;

        public RefCountAsyncDisposable(IAsyncDisposable disposable)
        {
            _disposable = disposable ?? throw new ArgumentNullException(nameof(disposable));
            _primaryDisposed = false;
            _count = 0;
        }

        public async ValueTask<IAsyncDisposable> GetDisposableAsync()
        {
            using (await _gate.LockAsync().ConfigureAwait(false))
            {
                if (_disposable == null)
                {
                    return AsyncDisposable.Nop;
                }
                else
                {
                    _count++;
                    return new Inner(this);
                }
            }

View on GitHub (pinned to 94b5d5ab91)