dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'cts')

Error message

Value cannot be null. (Parameter 'cts')

What it means

CancellationDisposable's constructor accepts an existing CancellationTokenSource and stores it as the source it will cancel on Dispose. A null cts has nothing to cancel, so the constructor throws ArgumentNullException immediately.

Solutions

  1. Ensure the CancellationTokenSource is initialized before constructing the disposable (e.g. cts ??= new CancellationTokenSource())
  2. If you have no existing source, call the parameterless constructor CancellationDisposable() which creates its own CTS
  3. If the value is legitimately nullable, guard: if (cts != null) new CancellationDisposable(cts); else new CancellationDisposable()

Example fix

// before
CancellationDisposable cd = new CancellationDisposable(_cts); // _cts may be null
// after
CancellationDisposable cd = new CancellationDisposable(_cts ?? new CancellationTokenSource());
Defensive patterns

Strategy: type-guard

Validate before calling

if (cts == null) cts = new CancellationTokenSource();
var cd = new CancellationDisposable(cts);

Type guard

CancellationDisposable SafeCreate(CancellationTokenSource? cts) => cts is null ? new CancellationDisposable() : new CancellationDisposable(cts);

Try / catch

try { return new CancellationDisposable(cts); }
catch (ArgumentNullException) { return new CancellationDisposable(); }

Prevention

When it happens

Trigger: new CancellationDisposable((CancellationTokenSource)null) — typically when the CTS comes from a field, property, method parameter, or linked-source helper that was never initialized.

Common situations: Passing a CancellationTokenSource obtained from an API that can return null; refactoring where a nullable CTS field is passed without a check.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Disposables/CancellationDisposable.cs:23

using System.Threading;

namespace System.Reactive.Disposables
{
    /// <summary>
    /// Represents a disposable resource that has an associated <seealso cref="CancellationToken"/> that will be set to the cancellation requested state upon disposal.
    /// </summary>
    public sealed class CancellationDisposable : ICancelable
    {
        private readonly CancellationTokenSource _cts;

        /// <summary>
        /// Initializes a new instance of the <see cref="CancellationDisposable"/> class that uses an existing <seealso cref="CancellationTokenSource"/>.
        /// </summary>
        /// <param name="cts"><seealso cref="CancellationTokenSource"/> used for cancellation.</param>
        /// <exception cref="ArgumentNullException"><paramref name="cts"/> is <c>null</c>.</exception>
        public CancellationDisposable(CancellationTokenSource cts)
        {
            _cts = cts ?? throw new ArgumentNullException(nameof(cts));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CancellationDisposable"/> class that uses a new <seealso cref="CancellationTokenSource"/>.
        /// </summary>
        public CancellationDisposable()
            : this(new CancellationTokenSource())
        {
        }

        /// <summary>
        /// Gets the <see cref="CancellationToken"/> used by this <see cref="CancellationDisposable"/>.
        /// </summary>
        public CancellationToken Token => _cts.Token;

        /// <summary>
        /// Cancels the underlying <seealso cref="CancellationTokenSource"/>.
        /// </summary>

View on GitHub (pinned to 94b5d5ab91)