dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'disposable')
Error message
Value cannot be null. (Parameter 'disposable')
What it means
SerialAsyncDisposable.AssignAsync throws ArgumentNullException when the disposable to assign is null. A serial disposable guarantees exactly one current child disposable at a time, and assigning null is disallowed — dispose the serial disposable itself instead of swapping in null.
Solutions
- Never pass null to AssignAsync; call DisposeAsync on the SerialAsyncDisposable itself to release the current child
- Fix the upstream factory/operator that returned null instead of a real (possibly no-op) disposable
- Guard the call site: 'if (d != null) await serial.AssignAsync(d); else await serial.DisposeAsync();'
- Use Disposable.Empty / a shared no-op IAsyncDisposable where 'nothing to dispose' is intended
Example fix
// before await serial.AssignAsync(GetSubscription()); // may return null // after var d = GetSubscription() ?? NopAsyncDisposable.Instance; await serial.AssignAsync(d);
Defensive patterns
Strategy: validation
Validate before calling
if (d is null)
{
await serial.DisposeAsync(); // clearing = disposing, never assigning null
}
else
{
await serial.AssignAsync(d);
} Type guard
bool IsAssignable([NotNullWhen(true)] IAsyncDisposable? d) => d is not null;
Try / catch
try
{
await serial.AssignAsync(d);
}
catch (ArgumentNullException ex) when (ex.ParamName == "disposable")
{
// factory returned null; fall back to disposing the serial disposable
await serial.DisposeAsync();
} Prevention
- Treat 'clear' as DisposeAsync, never as AssignAsync(null)
- Ensure subscription factories return a no-op disposable rather than null
- Null-check results of resource-acquisition helpers before assigning to serial disposable
When it happens
Trigger: Calling AssignAsync(null) directly; operator sinks (Buffer, Catch, Concat, DoWhile, For, OnErrorResumeNext) passing a null child disposable produced upstream, e.g. when a subscription factory returned null on a cancelled or failed path.
Common situations: A subscription helper returns null instead of a disposable when a source completes synchronously; passing the result of an optional resource acquisition without a null check; replacing an old pattern of assigning null to clear the serial disposable.
Related errors
- Value cannot be null. (Parameter 'disposable')
- Value cannot be null. (Parameter 'disposable')
- keySelector
- Value cannot be null. (Parameter 'keySelector')
- Value cannot be null. (Parameter 'comparer')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/8b50a16930eb2e74.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Disposables/SerialAsyncDisposable.cs:20
// 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 SerialAsyncDisposable : IAsyncDisposable
{
private readonly AsyncGate _gate = new();
private IAsyncDisposable _disposable;
private bool _disposed;
public async ValueTask AssignAsync(IAsyncDisposable disposable)
{
if (disposable == null)
throw new ArgumentNullException(nameof(disposable));
var shouldDispose = false;
var old = default(IAsyncDisposable);
using (await _gate.LockAsync().ConfigureAwait(false))
{
if (_disposed)
{
shouldDispose = true;
}
else
{
old = _disposable;
_disposable = disposable;
}
}
if (old != null)View on GitHub (pinned to 94b5d5ab91)