dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'context')

Error message

Value cannot be null. (Parameter 'context')

What it means

The ContextDisposable constructor throws ArgumentNullException when the context argument is null. ContextDisposable captures a SynchronizationContext on which Dispose will later be posted, so a null context would make the disposal behavior undefined. The constructor uses the ?? throw idiom to fail fast.

Solutions

  1. Pass an explicit context (e.g. a captured UI SynchronizationContext) instead of relying on SynchronizationContext.Current.
  2. Guard before constructing: var ctx = SynchronizationContext.Current ?? new SynchronizationContext();
  3. If no context is needed, use a plain disposable (Disposable.Create) instead of ContextDisposable.

Example fix

// before
var d = new ContextDisposable(SynchronizationContext.Current, resource);
// after
var ctx = SynchronizationContext.Current ?? new SynchronizationContext();
var d = new ContextDisposable(ctx, resource);
Defensive patterns

Strategy: validation

Validate before calling

var ctx = SynchronizationContext.Current ?? new SynchronizationContext();
var d = new ContextDisposable(ctx, resource);

Type guard

ContextDisposable TryCreate(SynchronizationContext ctx, IDisposable d) => ctx == null || d == null ? null : new ContextDisposable(ctx, d);

Try / catch

try { var d = new ContextDisposable(context, resource); } catch (ArgumentNullException ex) when (ex.ParamName == "context") { /* fall back to plain Disposable.Create */ }

Prevention

When it happens

Trigger: new ContextDisposable(null, someDisposable), typically when SynchronizationContext.Current is null (e.g. on a threadpool/console thread) and was passed through blindly.

Common situations: Capturing SynchronizationContext.Current in library code running without an installed context (console apps, ASP.NET Core, threadpool work items); UI code that lost its dispatcher context.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Disposables/ContextDisposable.cs:25

namespace System.Reactive.Disposables
{
    /// <summary>
    /// Represents a disposable resource whose disposal invocation will be posted to the specified <seealso cref="SynchronizationContext"/>.
    /// </summary>
    public sealed class ContextDisposable : ICancelable
    {
        private volatile IDisposable _disposable;

        /// <summary>
        /// Initializes a new instance of the <see cref="ContextDisposable"/> class that uses the specified <see cref="SynchronizationContext"/> on which to dispose the specified disposable resource.
        /// </summary>
        /// <param name="context">Context to perform disposal on.</param>
        /// <param name="disposable">Disposable whose Dispose operation to run on the given synchronization context.</param>
        /// <exception cref="ArgumentNullException"><paramref name="context"/> or <paramref name="disposable"/> is null.</exception>
        public ContextDisposable(SynchronizationContext context, IDisposable disposable)
        {
            Context = context ?? throw new ArgumentNullException(nameof(context));
            _disposable = disposable ?? throw new ArgumentNullException(nameof(disposable));
        }

        /// <summary>
        /// Gets the provided <see cref="SynchronizationContext"/>.
        /// </summary>
        public SynchronizationContext Context { get; }

        /// <summary>
        /// Gets a value that indicates whether the object is disposed.
        /// </summary>
        public bool IsDisposed => _disposable == BooleanDisposable.True;

        /// <summary>
        /// Disposes the underlying disposable on the provided <see cref="SynchronizationContext"/>.
        /// </summary>
        public void Dispose()
        {

View on GitHub (pinned to 94b5d5ab91)