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

  1. Enumerate the buffer before disposing it; keep disposal in a using scoped to the enumeration.
  2. Create a new Publish() buffer for each independent consumption lifetime instead of reusing a disposed one.
  3. 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

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


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)