redis/redis-py · error · ValueError
Cluster client has no nodes - cannot create health check…
Error message
Cluster client has no nodes - cannot create health check client
What it means
Raised as ValueError by AbstractHealthCheckPolicy.get_client() (healthcheck.py:208-211) when the database's underlying client is a RedisCluster but its startup_nodes collection is empty, so there is no host/port to seed the health-check cluster client from. The health-check layer cannot construct a probe client without at least one known node.
Solutions
- Ensure the cluster DatabaseConfig provides a reachable seed host/port (via from_url with a valid redis://cluster-host:port or host/port in client_kwargs).
- Verify the cluster is up and CLUSTER NODES returns entries before constructing MultiDBClient.
- Construct the RedisCluster client separately, confirm client.startup_nodes is non-empty, then pass it via DatabaseConfig.client_kwargs or a custom Database.
- Add a startup assertion: assert client.startup_nodes, 'cluster has no startup nodes'.
Example fix
// before
db_cfg = DatabaseConfig(client_kwargs={"host": ""}, ) # cluster, no seed node
# or a cluster whose discovery yielded nothing
// after - supply a valid cluster seed URL
from redis.asyncio import RedisCluster
rc = RedisCluster.from_url("redis://cluster-seed.local:6379")
assert rc.startup_nodes # fail fast with a clear cause
db_cfg = DatabaseConfig(client_kwargs={"startup_nodes": rc.startup_nodes}) Defensive patterns
Strategy: validation
Validate before calling
from redis.asyncio import RedisCluster
def cluster_has_startup_nodes(client_cls, kwargs, url) -> bool:
rc = RedisCluster.from_url(url) if url else RedisCluster(**kwargs)
return bool(rc.startup_nodes)
# before constructing MultiDBClient with a cluster DB:
assert cluster_has_startup_nodes(cfg.client_class, cfg.client_kwargs, cfg.from_url), \
"cluster database has no startup nodes" Type guard
from redis.asyncio import RedisCluster
def has_seed_nodes(rc: RedisCluster) -> bool:
return isinstance(rc, RedisCluster) and bool(rc.startup_nodes) Try / catch
try:
await client.initialize()
except ValueError as e:
if "no nodes" in str(e):
# fix the cluster seed host/port in DatabaseConfig and reconstruct
raise RuntimeError("cluster DatabaseConfig needs a reachable seed host/port")
raise Prevention
- Provide a reachable seed host/port for every cluster DatabaseConfig.
- Verify CLUSTER NODES returns entries before constructing MultiDBClient.
- Construct RedisCluster separately and assert startup_nodes is non-empty first.
- Do not pass a closed/stale cluster client whose nodes were cleared.
When it happens
Trigger: Configuring a DatabaseConfig with a cluster client (client_class=RedisCluster or a cluster URL) that was instantiated without usable startup nodes — e.g. constructed with an empty/malformed host list, a NodesManager that produced no startup nodes, or a cluster client that failed initial discovery and was then handed to MultiDBClient.
Common situations: Building RedisCluster from kwargs that omit host/port, pointing at a cluster URL whose CLUSTER NODES discovery returned nothing, race where the cluster client is passed to MultiDBClient before its node discovery completes, or a stale/closed cluster client whose startup_nodes were cleared.
Related errors
- health_check_probes must be greater than 0
- Unsupported client type
- Cannot set active database, database is unhealthy
- Given database is not a member of database list
- Initial connection failed - no active database found
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/592b7d9c2c5d9b4a.
Report an issue: GitHub.
Appendix: source
Thrown at redis/asyncio/multidb/healthcheck.py:209
# different names (``_require_full_coverage`` vs
# ``require_full_coverage``), so resolve it defensively to
# support a sync RedisCluster underlying client too.
require_full_coverage = getattr(
nodes_manager,
"require_full_coverage",
getattr(nodes_manager, "_require_full_coverage", True),
)
client = AsyncRedisCluster(
host=first_node.host,
port=first_node.port,
dynamic_startup_nodes=nodes_manager._dynamic_startup_nodes,
address_remap=nodes_manager.address_remap,
require_full_coverage=require_full_coverage,
retry=database.client.retry,
**filtered_kwargs,
)
else:
raise ValueError(
"Cluster client has no nodes - cannot create health check client"
)
else:
raise TypeError(f"Unsupported client type: {type(database.client)}")
self._clients[db_id] = client
return client
async def close(self) -> None:
"""Close all health check clients."""
close_tasks = [
asyncio.create_task(client.aclose()) for client in self._clients.values()
]
if close_tasks:
await asyncio.gather(*close_tasks, return_exceptions=True)
self._clients.clear()View on GitHub (pinned to 6a6b581b48)