{"record":{"id":"b4330993d815356f","repo":"dotnet/reactive","slug":"observer-join","errorCode":null,"errorMessage":"observer","messagePattern":"observer","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/Join.cs","lineNumber":52,"sourceCode":"                    var (leftObserver, rightObserver, disposable) = AsyncObserver.Join(observer, subscriptions, state.leftDurationSelector, state.rightDurationSelector, state.resultSelector);\n\n                    var leftSubscription = await left.SubscribeSafeAsync(leftObserver).ConfigureAwait(false);\n                    await subscriptions.AddAsync(leftSubscription).ConfigureAwait(false);\n\n                    var rightSubscription = await state.right.SubscribeSafeAsync(rightObserver).ConfigureAwait(false);\n                    await subscriptions.AddAsync(rightSubscription).ConfigureAwait(false);\n\n                    return disposable;\n                });\n        }\n    }\n\n    public partial class AsyncObserver\n    {\n        public static (IAsyncObserver<TLeft>, IAsyncObserver<TRight>, IAsyncDisposable) Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>(IAsyncObserver<TResult> observer, IAsyncDisposable subscriptions, Func<TLeft, IAsyncObservable<TLeftDuration>> leftDurationSelector, Func<TRight, IAsyncObservable<TRightDuration>> rightDurationSelector, Func<TLeft, TRight, TResult> resultSelector)\n        {\n            if (observer == null)\n                throw new ArgumentNullException(nameof(observer));\n            if (subscriptions == null)\n                throw new ArgumentNullException(nameof(subscriptions));\n            if (leftDurationSelector == null)\n                throw new ArgumentNullException(nameof(leftDurationSelector));\n            if (rightDurationSelector == null)\n                throw new ArgumentNullException(nameof(rightDurationSelector));\n            if (resultSelector == null)\n                throw new ArgumentNullException(nameof(resultSelector));\n\n            var gate = new AsyncGate();\n\n            var group = new CompositeAsyncDisposable(subscriptions);\n\n            var leftMap = new SortedDictionary<int, TLeft>();\n            var rightMap = new SortedDictionary<int, TRight>();\n\n            var leftDone = false;\n            var rightDone = false;","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/Join.cs#L34-L70","documentation":"The internal AsyncObserver.Join factory throws ArgumentNullException because the downstream IAsyncObserver<TResult> was null. This factory builds the paired left/right observers used by the Join operator; a null observer means results would have nowhere to go. The guard exists so the internal wiring always has a valid downstream sink.","triggerScenarios":"Calling AsyncObserver.Join(observer: null, subscriptions, leftDurationSelector, rightDurationSelector, resultSelector) — normally only reachable via custom operator plumbing that passes a null downstream observer.","commonSituations":"Custom operator implementations or test harnesses that construct AsyncObserver.Join directly and forget to supply the downstream observer, often because an outer observer variable was never initialized.","solutions":["Pass the actual downstream IAsyncObserver<TResult> instance as the first argument.","In custom operator code, ensure the observer received in your subscription lambda is forwarded, not discarded.","If you need a no-op sink for tests, provide a stub observer implementation rather than null."],"exampleFix":"// before\nvar (lo, ro, disp) = AsyncObserver.Join(null, subs, lds, rds, rs);\n// after\nvar (lo, ro, disp) = AsyncObserver.Join(downstreamObserver, subs, lds, rds, rs);","handlingStrategy":"validation","validationCode":"if (observer is null) throw new ArgumentException(\"downstream observer is required\", nameof(observer));","typeGuard":"bool ObserverReady<T>(IAsyncObserver<T>? o) => o is not null;","tryCatchPattern":"try { var parts = AsyncObserver.Join(observer, subs, lds, rds, rs); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"observer\") { /* fix custom plumbing to forward downstream observer */ }","preventionTips":["In custom operators, always forward the observer given to your Subscribe lambda.","Never pass null as an observer in tests; write a recording stub instead.","Keep internal factory calls close to the validated operator code path."],"tags":["argument-null","observer","internal-api"],"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"}