{"record":{"id":"278c8b3a0ba455c4","repo":"dotnet/reactive","slug":"cannot-access-a-disposed-object","errorCode":null,"errorMessage":"Cannot access a disposed object.","messagePattern":"Cannot access a disposed object\\.","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Internal/PushPullAdapter.cs","lineNumber":62,"sourceCode":"        {\n            get { return current.Value; }\n        }\n\n        public void Dispose()\n        {\n            disposed = true;\n            dispose();\n        }\n\n        object System.Collections.IEnumerator.Current\n        {\n            get { return this.Current; }\n        }\n\n        public bool MoveNext()\n        {\n            if (disposed)\n                throw new ObjectDisposedException(\"\");\n\n            if (!done)\n            {\n                current = moveNext();\n                done = current.Kind != NotificationKind.OnNext;\n            }\n\n            current.Exception?.Throw();\n\n            return current.HasValue;\n        }\n\n        public void Reset()\n        {\n            throw new NotSupportedException();\n        }\n    }\n}","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Internal/PushPullAdapter.cs#L44-L80","documentation":"PushPullAdapter.MoveNext() throws ObjectDisposedException once the adapter has been disposed. The adapter backs legacy IEnumerable-based observable bridges; iterating (MoveNext) after Dispose is treated as a bug because the underlying enumerator/subscription is already torn down.","triggerScenarios":"Calling MoveNext() on the adapter's enumerator after Dispose() was called — e.g. continuing to enumerate after the observable completed and was disposed, or enumerating a disposed subscription's adapter.","commonSituations":"Holding an IEnumerator across a dispose (using-block exited early), multi-threaded consumption where one thread disposes while another calls MoveNext, legacy code paths still using the #if-compiled PushPullAdapter.","solutions":["Stop enumerating once the subscription/adapter is disposed; dispose the enumerator inside a using block and do not reuse it.","Check for completion before continuing: when current notification is OnError/OnCompleted, stop calling MoveNext.","Guard concurrent access with a lock, or serialize dispose/enumerate on one thread."],"exampleFix":"// before\nvar en = adapter.GetEnumerator();\nadapter.Dispose();\nen.MoveNext();\n// after\nusing (var en = adapter.GetEnumerator())\n{\n    while (en.MoveNext()) { /* ... */ }\n} // disposed after enumeration ends","handlingStrategy":"try-catch","validationCode":"if (disposed) return; // track disposal state before enumerating","typeGuard":"static bool CanMoveNext(PushPullAdapter a) => !a.IsDisposed; // expose/track via wrapper","tryCatchPattern":"try { en.MoveNext(); } catch (ObjectDisposedException) { /* enumeration already ended */ }","preventionTips":["Wrap enumerator usage in using blocks and never use the enumerator after dispose.","Stop calling MoveNext after an OnError/OnCompleted notification.","Avoid sharing one adapter/enumerator across threads."],"tags":["object-disposed","enumeration","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}