{"record":{"id":"fa81e3e1a4a2a107","repo":"dotnet/reactive","slug":"eventloopscheduler","errorCode":null,"errorMessage":"EventLoopScheduler","messagePattern":"EventLoopScheduler","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Concurrency/EventLoopScheduler.cs","lineNumber":151,"sourceCode":"        /// <param name=\"dueTime\">Relative time after which to execute the action.</param>\n        /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"action\"/> is <c>null</c>.</exception>\n        /// <exception cref=\"ObjectDisposedException\">The scheduler has been disposed and doesn't accept new work.</exception>\n        public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)\n        {\n            if (action == null)\n            {\n                throw new ArgumentNullException(nameof(action));\n            }\n\n            var due = _stopwatch.Elapsed + dueTime;\n            var si = new ScheduledItem<TimeSpan, TState>(this, state, action, due);\n\n            lock (_gate)\n            {\n                if (_disposed)\n                {\n                    throw new ObjectDisposedException(nameof(EventLoopScheduler));\n                }\n\n                if (dueTime <= TimeSpan.Zero)\n                {\n                    _readyList.Enqueue(si);\n                    _evt.Release();\n                }\n                else\n                {\n                    _queue.Enqueue(si);\n                    _evt.Release();\n                }\n\n                EnsureThread();\n            }\n\n            return si;\n        }","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Concurrency/EventLoopScheduler.cs#L133-L169","documentation":"EventLoopScheduler.Schedule<TState>(state, dueTime, action) throws ObjectDisposedException(nameof(EventLoopScheduler)) when the action is enqueued after the scheduler was disposed. Disposal sets _disposed under _gate; any new work would never run because the event loop thread has exited, so the scheduler rejects it explicitly.","triggerScenarios":"Calling any Schedule/SchedulePeriodic overload on an EventLoopScheduler after Dispose() was called — typically a long-lived subscription or timer whose observer fires after shutdown, or a periodic work item re-scheduling itself post-dispose.","commonSituations":"Application shutdown / DI container disposal order issues where the scheduler is disposed while active Rx subscriptions still attempt to schedule; unit tests disposing the scheduler in TearDown while async callbacks are still pending.","solutions":["Dispose subscriptions (IDsisposable from Subscribe) before disposing the scheduler.","Serialize shutdown: cancel/signal work first, then dispose the scheduler last.","Wrap scheduling calls and swallow/ignore ObjectDisposedException during shutdown where acceptable.","Create a new EventLoopScheduler if you intentionally disposed and need to schedule again — it cannot be revived."],"exampleFix":"// before\nscheduler.Dispose();\nscheduler.Schedule(0, TimeSpan.Zero, s => Disposable.Empty); // throws\n\n// after\nsubscription.Dispose();\nscheduler.Dispose();\nif (!scheduler.IsDisposed) scheduler.Schedule(0, TimeSpan.Zero, s => Disposable.Empty);","handlingStrategy":"try-catch","validationCode":"if (schedulerIsDisposed) return Disposable.Empty; // track disposal yourself\nscheduler.Schedule(state, dueTime, action);","typeGuard":null,"tryCatchPattern":"try { return scheduler.Schedule(state, dueTime, action); }\ncatch (ObjectDisposedException)\n{\n    return Disposable.Empty; // benign during shutdown\n}","preventionTips":["Dispose Rx subscriptions before disposing the scheduler","Use a shutdown barrier/Task to wait for in-flight work before Dispose","Track scheduler lifetime with a CancellationToken passed to work items"],"tags":["object-disposed","scheduler","lifecycle"],"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-15T23:17:13.987Z"}