{"record":{"id":"483a4eccce96a941","repo":"redis/redis-py","slug":"scheduler-is-stopped","errorCode":null,"errorMessage":"Scheduler is stopped","messagePattern":"Scheduler is stopped","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"redis/background.py","lineNumber":143,"sourceCode":"\n        Args:\n            coro: Coroutine function to execute\n            *args: Arguments to pass to the coroutine\n            timeout: Maximum seconds to wait for the result. None means wait\n                forever. Default is 10 seconds to avoid blocking indefinitely\n                if the event loop is busy with long-running health checks.\n\n        Returns:\n            The result of the coroutine\n\n        Raises:\n            TimeoutError: If the coroutine doesn't complete within timeout\n            Any exception raised by the coroutine\n        \"\"\"\n\n        with self._lock:\n            if self._stopped:\n                raise RuntimeError(\"Scheduler is stopped\")\n\n        # Ensure the shared loop exists\n        self._ensure_health_check_loop()\n\n        with self._lock:\n            loop = self._health_check_loop\n\n        # Submit the coroutine to the shared loop and wait for result\n        future = asyncio.run_coroutine_threadsafe(coro(*args), loop)\n        try:\n            return future.result(timeout=timeout)\n        except TimeoutError:\n            # Cancel the future to avoid leaving orphaned tasks\n            future.cancel()\n            raise\n\n    def run_coro_fire_and_forget(\n        self, coro: Callable[..., Coroutine[Any, Any, Any]], *args","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/background.py#L125-L161","documentation":"Raised as RuntimeError in BackgroundScheduler.run_coro_sync when self._stopped is True. The scheduler has been shut down (stop() was called, or __del__ ran), so it can no longer submit coroutines to its event loop. This protects against scheduling work on a dead scheduler.","triggerScenarios":"Calling run_coro_sync(...) on a BackgroundScheduler after stop() has been invoked. This happens in multidb health-check/circuit-breaker paths when a health check is attempted after the client or scheduler was shut down, or during interpreter teardown when __del__ already stopped it.","commonSituations":"Application shutdown ordering: the client/scheduler was stopped before a final health check fired. A background thread attempting a health check racing with explicit stop(). Interpreter exit triggering __del__ while a thread is still using the scheduler.","solutions":["Ensure health checks are not scheduled after calling stop() on the scheduler/client (fix shutdown ordering).","Guard callers with a check of scheduler._stopped (or a public is_running flag) before submitting work.","Cancel pending health-check timers before stopping the scheduler."],"exampleFix":"# before\nawait client.close()  # stops scheduler\nawait run_health_check()  # run_coro_sync -> RuntimeError\n# after\nawait client.close()  # stops scheduler\n# do not schedule further work after close","handlingStrategy":"validation","validationCode":"if scheduler._stopped:\n    logger.debug(\"scheduler stopped; skipping health check\")\nelse:\n    scheduler.run_coro_sync(hc, client)","typeGuard":"def scheduler_alive(scheduler) -> bool:\n    return not getattr(scheduler, \"_stopped\", True)","tryCatchPattern":"try:\n    scheduler.run_coro_sync(hc, client)\nexcept RuntimeError as e:\n    if \"Scheduler is stopped\" in str(e):\n        logger.debug(\"scheduler stopped; ignoring health check\")\n    else:\n        raise","preventionTips":["Order shutdown so health-check timers are cancelled before stop().","Avoid issuing work after client.close()/scheduler.stop()."],"tags":["background","scheduler","lifecycle","shutdown"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}