{"record":{"id":"42c4c7fc0ae1a567","repo":"dotnet/reactive","slug":"strings-core-reentrancy-detected","errorCode":null,"errorMessage":"Strings_Core.REENTRANCY_DETECTED","messagePattern":"Strings_Core\\.REENTRANCY_DETECTED","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Internal/CheckedObserver.cs","lineNumber":70,"sourceCode":"        {\n            CheckAccess();\n\n            try\n            {\n                _observer.OnCompleted();\n            }\n            finally\n            {\n                Interlocked.Exchange(ref _state, Done);\n            }\n        }\n\n        private void CheckAccess()\n        {\n            switch (Interlocked.CompareExchange(ref _state, Busy, Idle))\n            {\n                case Busy:\n                    throw new InvalidOperationException(Strings_Core.REENTRANCY_DETECTED);\n                case Done:\n                    throw new InvalidOperationException(Strings_Core.OBSERVER_TERMINATED);\n            }\n        }\n    }\n}\n","sourceCodeStart":52,"sourceCodeEnd":77,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Internal/CheckedObserver.cs#L52-L77","documentation":"CheckedObserver is a diagnostic wrapper that detects reentrant notifications. CheckAccess uses an Interlocked.CompareExchange state machine (Idle/Busy/Done); if a callback (OnNext/OnError/OnCompleted) is invoked while the same observer is already inside a notification (state Busy), it throws InvalidOperationException(REENTRANCY_DETECTED).","triggerScenarios":"Calling OnNext/OnError/OnCompleted on the observer from within one of its own callbacks, or concurrently from another thread while a notification is in flight. Typically occurs when using Observable.Create with a CheckedObserver in CreateCheckedObserver-based tests or instrumented pipelines.","commonSituations":"Subjects/subscribers whose OnNext re-triggers the same observer (circular reactive graphs); shared observers called from multiple threads; debug/instrumentation builds where CheckedObserver wraps production observers.","solutions":["Restructure the notification graph so the observer never re-enters itself (break the cycle, defer with ObserveOn or a Scheduler).","Ensure notifications are serialized to one thread at a time (e.g. ObserveOn a single-threaded context) if concurrency is the cause.","Remove or replace the CheckedObserver wrapper if you only needed it for debugging."],"exampleFix":"// before\nsource.Subscribe(x => subject.OnNext(x)); // subject feeds back into same observer\n// after\nsource.ObserveOn(TaskPoolScheduler.Default).Subscribe(x => { if (x != last) subject.OnNext(x); });","handlingStrategy":"try-catch","validationCode":"// track in-flight state yourself before re-notifying\nprivate int _inFlight;\nbool CanNotify() => Interlocked.CompareExchange(ref _inFlight, 1, 0) == 0;","typeGuard":"bool IsReentrantCall() => _insideNotification; // set true at callback entry, false at exit","tryCatchPattern":"try { observer.OnNext(value); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"Reentrancy\")) { /* defer via scheduler or break the cycle */ }","preventionTips":["Never call the same observer from inside its own OnNext callback.","Serialize notifications with ObserveOn on a single-threaded scheduler.","Defer feedback loops through Subject/Scheduler instead of direct calls."],"tags":["reentrancy","observer","concurrency","invalid-operation"],"backgroundTag":"invalid-state-transition","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}