dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'disposable')

Error message

Value cannot be null. (Parameter 'disposable')

What it means

The RefCountDisposable constructor requires a non-null underlying IDisposable; System.Reactive explicitly guards it with a null check and throws ArgumentNullException. The reference-counting wrapper cannot function without a disposable to ref-count, so a null argument is treated as a programming error rather than silently tolerated.

Solutions

  1. Ensure the underlying IDisposable is created before constructing the RefCountDisposable.
  2. Use Disposable.Empty as an explicit non-null placeholder when there is nothing to dispose.
  3. Guard with a null check and fall back to Disposable.Empty or skip creating the RefCountDisposable entirely.

Example fix

// before
var refCount = new RefCountDisposable(GetInner()); // GetInner() may return null
// after
var inner = GetInner() ?? Disposable.Empty;
var refCount = new RefCountDisposable(inner);
Defensive patterns

Strategy: validation

Validate before calling

if (inner is null) throw new InvalidOperationException("Underlying disposable must be created before RefCountDisposable");
var rc = new RefCountDisposable(inner);

Type guard

static bool IsUsable(IDisposable d) => d is not null;

Try / catch

try { var rc = new RefCountDisposable(inner); }
catch (ArgumentNullException ex) when (ex.ParamName == "disposable") { /* fall back to Disposable.Empty semantics */ }

Prevention

When it happens

Trigger: Calling new RefCountDisposable(null) or new RefCountDisposable(null, throwWhenDisposed) — i.e. passing a null IDisposable as the 'disposable' parameter, typically when a lazy/deferred producer of the underlying disposable returned null.

Common situations: Passing the result of a factory method or dictionary lookup that returned null (e.g. GetDisposable() from another container), conditional initialization where the inner disposable was never created, or refactoring that changed the order of assignment to a backing field.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Disposables/RefCountDisposable.cs:42

        /// <summary>
        /// Initializes a new instance of the <see cref="RefCountDisposable"/> class with the specified disposable.
        /// </summary>
        /// <param name="disposable">Underlying disposable.</param>
        /// <exception cref="ArgumentNullException"><paramref name="disposable"/> is null.</exception>
        public RefCountDisposable(IDisposable disposable) : this(disposable, false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RefCountDisposable"/> class with the specified disposable.
        /// </summary>
        /// <param name="disposable">Underlying disposable.</param>
        /// <param name="throwWhenDisposed">Indicates whether subsequent calls to <see cref="GetDisposable"/> should throw when this instance is disposed.</param>
        /// <exception cref="ArgumentNullException"><paramref name="disposable"/> is null.</exception>
        public RefCountDisposable(IDisposable disposable, bool throwWhenDisposed)
        {
            _disposable = disposable ?? throw new ArgumentNullException(nameof(disposable));
            _count = 0;
            _throwWhenDisposed = throwWhenDisposed;
        }

        /// <summary>
        /// Gets a value that indicates whether the object is disposed.
        /// </summary>
        public bool IsDisposed => Volatile.Read(ref _count) == int.MinValue;

        /// <summary>
        /// Returns a dependent disposable that when disposed decreases the refcount on the underlying disposable.
        /// </summary>
        /// <returns>A dependent disposable contributing to the reference count that manages the underlying disposable's lifetime.</returns>
        /// <exception cref="ObjectDisposedException">This instance has been disposed and is configured to throw in this case by <see cref="RefCountDisposable(IDisposable, bool)"/>.</exception>
        public IDisposable GetDisposable()
        {
            // the current state
            var cnt = Volatile.Read(ref _count);

View on GitHub (pinned to 94b5d5ab91)