dotnet/reactive · error · InvalidOperationException

Strings_Core.OBSERVER_TERMINATED

Error message

Strings_Core.OBSERVER_TERMINATED

What it means

CheckedObserver marks itself Done once a terminal notification (OnError/OnCompleted) has been delivered. Any further OnNext/OnError/OnCompleted after termination hits the Done case in CheckAccess and throws InvalidOperationException(OBSERVER_TERMINATED), enforcing the IObservable grammar that an observer receives at most one terminal event.

Solutions

  1. Stop sending notifications after OnError/OnCompleted (return from the producer loop).
  2. Route late events through a Subject that is recreated per sequence instead of reusing a terminated observer.
  3. Check a completion flag in your producing code before calling OnNext.

Example fix

// before
if (_cache) _observer.OnNext(value);
// after
if (_cache && !_completed) _observer.OnNext(value);
Defensive patterns

Strategy: validation

Validate before calling

private bool _terminated;
void SafeOnNext(IObserver<T> o, T v) { if (!_terminated) o.OnNext(v); }

Type guard

bool IsAlive(IObserver<T> o) => !_completed && !_errored; // track terminal state in your producer

Try / catch

try { observer.OnNext(value); } catch (InvalidOperationException ex) when (ex.Message.Contains("terminated")) { /* stop producing; observer is done */ }

Prevention

When it happens

Trigger: Invoking OnNext/OnError/OnCompleted on a CheckedObserver after OnError or OnCompleted was already called, e.g. holding the observer and calling OnNext in a loop after completion, or a Subject delivering events after termination.

Common situations: Publishing to a cached subject/observer after Dispose; custom operators that emit after OnCompleted; long-lived handlers stored in fields that keep firing after the sequence ended.

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/0711220ee5e154e5. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Internal/CheckedObserver.cs:72

            try
            {
                _observer.OnCompleted();
            }
            finally
            {
                Interlocked.Exchange(ref _state, Done);
            }
        }

        private void CheckAccess()
        {
            switch (Interlocked.CompareExchange(ref _state, Busy, Idle))
            {
                case Busy:
                    throw new InvalidOperationException(Strings_Core.REENTRANCY_DETECTED);
                case Done:
                    throw new InvalidOperationException(Strings_Core.OBSERVER_TERMINATED);
            }
        }
    }
}

View on GitHub (pinned to 94b5d5ab91)