dotnet/reactive · error · ObjectDisposedException
Cannot access a disposed object. Object name: ''.
Error message
Cannot access a disposed object. Object name: ''.
What it means
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.
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).
Example fix
// before
var buffer = source.Publish();
buffer.Dispose();
foreach (var x in buffer) { } // ObjectDisposedException
// after
using (var buffer = source.Publish())
{
foreach (var x in buffer) { }
} Defensive patterns
Strategy: try-catch
Validate before calling
if (buffer is IDisposable d && isDisposed) buffer = source.Publish(); // re-publish instead of reusing
Type guard
static bool IsUsable<T>(IBuffer<T> buffer) => buffer is not null; // disposal state is internal; guard by ownership discipline
Try / catch
try
{
foreach (var x in buffer) { }
}
catch (ObjectDisposedException)
{
buffer = source.Publish(); // recreate and retry once
} Prevention
- 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.
When it happens
Trigger: Calling GetEnumerator() (directly or via foreach) on the IBuffer<T> returned by Publish() after buffer.Dispose() has run.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Value cannot be null. (Parameter 'func')
- Value cannot be null. (Parameter 'resultSelector')
- new ArgumentNullException(nameof(comparer))
- new ArgumentOutOfRangeException(nameof(capacity))
- ArgumentOutOfRangeException: capacity
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/24a323942fa23ebe.
Report an issue: GitHub.
Appendix: source
Thrown at Ix.NET/Source/System.Interactive/System/Linq/Operators/Publish.cs:82
private readonly object _gate = new();
private readonly RefCountList<T> _buffer;
private readonly IEnumerator<T> _source;
private bool _disposed;
private Exception? _error;
private bool _stopped;
public PublishedBuffer(IEnumerator<T> source)
{
_buffer = new RefCountList<T>(0);
_source = source;
}
public IEnumerator<T> GetEnumerator()
{
if (_disposed)
{
throw new ObjectDisposedException("");
}
var i = default(int);
lock (_gate)
{
i = _buffer.Count;
_buffer.ReaderCount++;
}
return GetEnumeratorCore(i);
}
IEnumerator IEnumerable.GetEnumerator()
{
if (_disposed)
{
throw new ObjectDisposedException("");
}View on GitHub (pinned to 94b5d5ab91)