{"record":{"id":"8b50a16930eb2e74","repo":"dotnet/reactive","slug":"value-cannot-be-null-parameter-disposable-8b50a1","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'disposable')","messagePattern":"Value cannot be null\\. \\(Parameter 'disposable'\\)","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Disposables/SerialAsyncDisposable.cs","lineNumber":20,"sourceCode":"// The .NET Foundation licenses this file to you under the MIT License.\n// See the LICENSE file in the project root for more information. \n\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace System.Reactive.Disposables\n{\n    public sealed class SerialAsyncDisposable : IAsyncDisposable\n    {\n        private readonly AsyncGate _gate = new();\n\n        private IAsyncDisposable _disposable;\n        private bool _disposed;\n\n        public async ValueTask AssignAsync(IAsyncDisposable disposable)\n        {\n            if (disposable == null)\n                throw new ArgumentNullException(nameof(disposable));\n\n            var shouldDispose = false;\n            var old = default(IAsyncDisposable);\n\n            using (await _gate.LockAsync().ConfigureAwait(false))\n            {\n                if (_disposed)\n                {\n                    shouldDispose = true;\n                }\n                else\n                {\n                    old = _disposable;\n                    _disposable = disposable;\n                }\n            }\n\n            if (old != null)","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Disposables/SerialAsyncDisposable.cs#L2-L38","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait serial.AssignAsync(GetSubscription()); // may return null\n// after\nvar d = GetSubscription() ?? NopAsyncDisposable.Instance;\nawait serial.AssignAsync(d);","handlingStrategy":"validation","validationCode":"if (d is null)\n{\n    await serial.DisposeAsync(); // clearing = disposing, never assigning null\n}\nelse\n{\n    await serial.AssignAsync(d);\n}","typeGuard":"bool IsAssignable([NotNullWhen(true)] IAsyncDisposable? d) => d is not null;","tryCatchPattern":"try\n{\n    await serial.AssignAsync(d);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"disposable\")\n{\n    // factory returned null; fall back to disposing the serial disposable\n    await serial.DisposeAsync();\n}","preventionTips":["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"],"tags":["csharp","null-reference","async-disposable","argument-validation"],"backgroundTag":"null-argument","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}