dotnet/reactive · error · ObjectDisposedException

Cannot access a disposed object. Object name: ''.

Error message

Cannot access a disposed object.
Object name: ''.

What it means

MemoizedBuffer.GetEnumerator checks the buffer's _disposed flag and throws ObjectDisposedException once Dispose() has been called on the buffer. After disposal, new enumerators can no longer be handed out, so requesting one throws "Cannot access a disposed object".

Solutions

  1. Keep the buffer alive (dispose it) only after all consumers have finished enumerating.
  2. Obtain all enumerators inside the lifetime of the buffer's using block.
  3. If lazy consumption is needed, materialize with ToList/ToArray before disposing.

Example fix

// before
IBuffer<int> buffer;
using (buffer = source.Memoize()) { }
foreach (var x in buffer) { } // throws
// after
using (var buffer = source.Memoize())
{
    foreach (var x in buffer) { }
}
Defensive patterns

Strategy: try-catch

Validate before calling

bool usable = buffer is IBuffer<T> b && !IsDisposed(b); // track disposal via your own flag since _disposed is private
if (!usable) { /* re-memoize or fail fast */ }

Type guard

static bool CanEnumerate<T>(IBuffer<T> buffer, HashSet<object> disposed) => !disposed.Contains(buffer);

Try / catch

try { foreach (var x in buffer) Process(x); }
catch (ObjectDisposedException) { buffer = source.Memoize(); foreach (var x in buffer) Process(x); }

Prevention

When it happens

Trigger: Calling GetEnumerator() on the IBuffer returned by Memoize after the buffer (or all of its leases) has been disposed — e.g. enumerating the buffer after a using block over the buffer ended, or after the owning scope disposed it.

Common situations: Storing the IBuffer in a field or cache while disposing it in a using statement; concurrent consumers starting enumeration after the producer disposed the buffer; returning the buffer from a method that disposes it before the caller enumerates.

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/0bab97fd58190bf2. Report an issue: GitHub.

Appendix: source

Thrown at Ix.NET/Source/System.Interactive/System/Linq/Operators/Memoize.cs:143

                : this(source, new MaxRefCountList<T>())
            {
            }

            public MemoizedBuffer(IEnumerator<T> source, int readerCount)
                : this(source, new RefCountList<T>(readerCount))
            {
            }

            private MemoizedBuffer(IEnumerator<T> source, IRefCountList<T> buffer)
            {
                _source = source;
                _buffer = buffer;
            }

            public IEnumerator<T> GetEnumerator()
            {
                if (_disposed)
                    throw new ObjectDisposedException("");

                return GetEnumerator_();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                if (_disposed)
                    throw new ObjectDisposedException("");

                return GetEnumerator();
            }

            public void Dispose()
            {
                lock (_gate)
                {
                    if (!_disposed)
                    {

View on GitHub (pinned to 94b5d5ab91)