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
- Stop sending notifications after OnError/OnCompleted (return from the producer loop).
- Route late events through a Subject that is recreated per sequence instead of reusing a terminated observer.
- 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
- Set a completed/errored flag in OnCompleted/OnError wrappers and check it before further notifications.
- Do not retain observers in long-lived producers after the sequence terminates.
- Recreate subjects/observers per subscription instead of reusing them.
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
- Strings_Core.REENTRANCY_DETECTED
- Value cannot be null. (Parameter 'observer')
- Disposable already assigned.
- Value cannot be null. (Parameter 'onNextAsync')
- Value cannot be null. (Parameter 'onErrorAsync')
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)