dotnet/reactive · error · ObjectDisposedException

Cannot access a disposed object.

Error message

Cannot access a disposed object.

What it means

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.

Solutions

  1. Stop enumerating once the subscription/adapter is disposed; dispose the enumerator inside a using block and do not reuse it.
  2. Check for completion before continuing: when current notification is OnError/OnCompleted, stop calling MoveNext.
  3. Guard concurrent access with a lock, or serialize dispose/enumerate on one thread.

Example fix

// before
var en = adapter.GetEnumerator();
adapter.Dispose();
en.MoveNext();
// after
using (var en = adapter.GetEnumerator())
{
    while (en.MoveNext()) { /* ... */ }
} // disposed after enumeration ends
Defensive patterns

Strategy: try-catch

Validate before calling

if (disposed) return; // track disposal state before enumerating

Type guard

static bool CanMoveNext(PushPullAdapter a) => !a.IsDisposed; // expose/track via wrapper

Try / catch

try { en.MoveNext(); } catch (ObjectDisposedException) { /* enumeration already ended */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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/278c8b3a0ba455c4. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Internal/PushPullAdapter.cs:62

        {
            get { return current.Value; }
        }

        public void Dispose()
        {
            disposed = true;
            dispose();
        }

        object System.Collections.IEnumerator.Current
        {
            get { return this.Current; }
        }

        public bool MoveNext()
        {
            if (disposed)
                throw new ObjectDisposedException("");

            if (!done)
            {
                current = moveNext();
                done = current.Kind != NotificationKind.OnNext;
            }

            current.Exception?.Throw();

            return current.HasValue;
        }

        public void Reset()
        {
            throw new NotSupportedException();
        }
    }
}

View on GitHub (pinned to 94b5d5ab91)