{"record":{"id":"8e5bdefacfbf7247","repo":"dotnet/reactive","slug":"value-cannot-be-null-parameter-disposable-8e5bde","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/SingleAssignmentAsyncDisposable.cs","lineNumber":19,"sourceCode":"﻿// Licensed to the .NET Foundation under one or more agreements.\n// 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 SingleAssignmentAsyncDisposable : IAsyncDisposable\n    {\n        private static readonly IAsyncDisposable Disposed = AsyncDisposable.Create(() => default);\n\n        private IAsyncDisposable _disposable;\n\n        public async ValueTask AssignAsync(IAsyncDisposable disposable)\n        {\n            if (disposable == null)\n                throw new ArgumentNullException(nameof(disposable));\n\n            var old = Interlocked.CompareExchange(ref _disposable, disposable, null);\n\n            if (old == null)\n                return;\n\n            if (old != Disposed)\n                throw new InvalidOperationException(\"Disposable already assigned.\");\n\n            await disposable.DisposeAsync().ConfigureAwait(false);\n        }\n\n        public ValueTask DisposeAsync()\n        {\n            return Interlocked.Exchange(ref _disposable, Disposed)?.DisposeAsync() ?? default;\n        }\n    }\n}","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Disposables/SingleAssignmentAsyncDisposable.cs#L1-L37","documentation":"SingleAssignmentAsyncDisposable.AssignAsync throws ArgumentNullException when the disposable passed is null. The class allows exactly one assignment (implemented via Interlocked.CompareExchange against null), and a null argument is rejected before any exchange is attempted.","triggerScenarios":"Calling AssignAsync(null) directly; callers listed in the region (firstTask/secondTask races, Append, Buffer, DoWhile sinks) passing a null disposable produced by an upstream factory or a not-yet-initialized field.","commonSituations":"A subscription/resource factory returns null on a failure or cancellation path; passing a nullable field that was never set; tests exercising the sink before wiring the real disposable.","solutions":["Ensure a real IAsyncDisposable (or a no-op) is produced before assigning","Guard at the call site: 'if (d != null) await single.AssignAsync(d);'","Fix the factory returning null so it returns an empty/no-op disposable instead","If the sink may or may not need a disposable, conditionally skip the AssignAsync call"],"exampleFix":"// before\nawait single.AssignAsync(_subscription); // _subscription is null on cancel path\n// after\nif (_subscription != null)\n{\n    await single.AssignAsync(_subscription);\n}","handlingStrategy":"validation","validationCode":"if (d is null)\n    throw new InvalidOperationException(\"Refusing to assign a null disposable.\");\nawait single.AssignAsync(d);","typeGuard":"bool CanAssign([NotNullWhen(true)] IAsyncDisposable? d) => d is not null;","tryCatchPattern":"try\n{\n    await single.AssignAsync(d);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"disposable\")\n{\n    // subscription factory produced null; use a no-op disposable or skip assignment\n}","preventionTips":["Replace nullable disposable fields with non-null defaults (no-op disposable)","Null-check any factory output before handing it to AssignAsync","Keep assignment paths free of conditional resource acquisition that can yield null"],"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"}