{"record":{"id":"24a323942fa23ebe","repo":"dotnet/reactive","slug":"cannot-access-a-disposed-object-object-name-publish","errorCode":null,"errorMessage":"Cannot access a disposed object.\nObject name: ''.","messagePattern":"Cannot access a disposed object\\.\nObject name: ''\\.","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"Ix.NET/Source/System.Interactive/System/Linq/Operators/Publish.cs","lineNumber":82,"sourceCode":"            private readonly object _gate = new();\n            private readonly RefCountList<T> _buffer;\n            private readonly IEnumerator<T> _source;\n\n            private bool _disposed;\n            private Exception? _error;\n            private bool _stopped;\n\n            public PublishedBuffer(IEnumerator<T> source)\n            {\n                _buffer = new RefCountList<T>(0);\n                _source = source;\n            }\n\n            public IEnumerator<T> GetEnumerator()\n            {\n                if (_disposed)\n                {\n                    throw new ObjectDisposedException(\"\");\n                }\n\n                var i = default(int);\n                lock (_gate)\n                {\n                    i = _buffer.Count;\n                    _buffer.ReaderCount++;\n                }\n\n                return GetEnumeratorCore(i);\n            }\n\n            IEnumerator IEnumerable.GetEnumerator()\n            {\n                if (_disposed)\n                {\n                    throw new ObjectDisposedException(\"\");\n                }","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Ix.NET/Source/System.Interactive/System/Linq/Operators/Publish.cs#L64-L100","documentation":"PublishedBuffer<T>.GetEnumerator throws ObjectDisposedException once the buffer's Dispose() has been called. The published view is a shared, single-lifetime resource; enumerating it after disposal is invalid because the underlying enumerator and buffer are torn down.","triggerScenarios":"Calling GetEnumerator() (directly or via foreach) on the IBuffer<T> returned by Publish() after buffer.Dispose() has run.","commonSituations":"Storing the buffer for later use while a using block or timeout disposes it; caching the published buffer across requests; enumerating a shared buffer concurrently while another thread disposes it.","solutions":["Enumerate the buffer before disposing it; keep disposal in a using scoped to the enumeration.","Create a new Publish() buffer for each independent consumption lifetime instead of reusing a disposed one.","Synchronize: ensure the thread that disposes is not racing an in-flight enumeration (check _disposed or use locking at the call site)."],"exampleFix":"// before\nvar buffer = source.Publish();\nbuffer.Dispose();\nforeach (var x in buffer) { } // ObjectDisposedException\n// after\nusing (var buffer = source.Publish())\n{\n    foreach (var x in buffer) { }\n}","handlingStrategy":"try-catch","validationCode":"if (buffer is IDisposable d && isDisposed) buffer = source.Publish(); // re-publish instead of reusing","typeGuard":"static bool IsUsable<T>(IBuffer<T> buffer) => buffer is not null; // disposal state is internal; guard by ownership discipline","tryCatchPattern":"try\n{\n    foreach (var x in buffer) { }\n}\ncatch (ObjectDisposedException)\n{\n    buffer = source.Publish(); // recreate and retry once\n}","preventionTips":["Always scope the buffer in a using block that encloses all enumeration.","Never store PublishedBuffer instances in long-lived fields or caches.","Publish one buffer per consumer lifetime instead of sharing across scopes."],"tags":["disposed-object","lifecycle","linq"],"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-15T23:17:13.987Z"}