redis/redis-py · error · RuntimeError
Health check event loop failed to start within
Error message
Health check event loop failed to start within {timeout} seconds What it means
Raised as RuntimeError in BackgroundScheduler._ensure_health_check_loop when the health-check event loop fails to signal readiness within the timeout (default 5.0s). The background thread is supposed to set _health_check_loop_ready once asyncio.run_forever begins; if it never does, the loop is unusable. The code cleans up the failed loop so a retry can attempt a fresh one.
Solutions
- Retry the operation — _ensure_health_check_loop cleans up the failed loop and a subsequent call creates a new one.
- Reduce system load or raise thread/resource limits (ulimit -u) so the daemon thread can start promptly.
- Increase the readiness timeout by calling a code path that passes a larger timeout if exposed, or investigate _run_health_check_loop for exceptions.
- Check logs for exceptions from the health-check thread that prevented readiness signaling.
Example fix
# before: single attempt fails under load
result = scheduler.run_coro_sync(hc, client, timeout=10) # RuntimeError
# after: retry with backoff
for attempt in range(3):
try:
result = scheduler.run_coro_sync(hc, client, timeout=10)
break
except RuntimeError:
time.sleep(1) Defensive patterns
Strategy: retry
Try / catch
for attempt in range(3):
try:
return scheduler.run_coro_sync(hc, client, timeout=10)
except RuntimeError as e:
if "failed to start" in str(e):
await asyncio.sleep(backoff)
continue
raise Prevention
- Raise thread/resource limits on hosts running the background scheduler.
- Retry loop startup — the failed loop is cleaned up for reuse.
When it happens
Trigger: run_coro_sync or run_coro_fire_and_forget triggers _ensure_health_check_loop, the daemon thread is started, but _health_check_loop_ready.wait(timeout) returns False — the thread never reached the set() call, e.g. thread startup blocked, loop creation failed, or the system is starved of resources.
Common situations: System under extreme load / thread starvation so the daemon thread does not get scheduled within 5s. Thread creation blocked by resource limits (ulimit -u) or a restricted runtime. A bug or exception inside _run_health_check_loop before it sets the ready event. CI environment with constrained CPU.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Channel: ' ' has no handler registered
- Connection closed by server.
- No connection available.
- Pattern: ' ' has no handler registered
- Scheduler is stopped
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/510feee8de97ce34.
Report an issue: GitHub.
Appendix: source
Thrown at redis/background.py:254
)
self._health_check_thread.start()
# Wait for loop to be running INSIDE the lock with a timeout.
# This prevents other threads from trying to create another loop
# before this one is fully started, while avoiding permanent deadlock
# if the background thread fails to start the loop.
if not self._health_check_loop_ready.wait(timeout=timeout):
# Timeout expired - the loop failed to start
# Clean up the failed loop to allow retry
failed_loop = self._health_check_loop
self._health_check_loop = None
if failed_loop in self._event_loops:
self._event_loops.remove(failed_loop)
try:
failed_loop.close()
except Exception:
pass
raise RuntimeError(
f"Health check event loop failed to start within {timeout} seconds"
)
def _run_health_check_loop(self):
"""Run the shared health check event loop."""
asyncio.set_event_loop(self._health_check_loop)
# Signal that the loop is ready before running
# Use call_soon to signal after run_forever starts processing
self._health_check_loop.call_soon(self._health_check_loop_ready.set)
try:
self._health_check_loop.run_forever()
finally:
try:
pending = asyncio.all_tasks(self._health_check_loop)
for task in pending:
task.cancel()View on GitHub (pinned to 6a6b581b48)