redis/redis-py · warning · ConnectionError
Failed to subscribe to cluster nodes: {', '.join(failed_node
Error message
Failed to subscribe to cluster nodes: {', '.join(failed_nodes)} What it means
Raised as ConnectionError by ClusterKeyspaceNotifications.refresh_subscriptions() when one or more newly discovered primary nodes could not be subscribed to. The method attempts every new node, collects failures, then raises once with the list of failed node names. Existing subscriptions and cleanup of failed nodes still happen; the raise signals that the topology is not fully subscribed.
Source
Thrown at redis/asyncio/keyspace_notifications.py:1019
new_nodes = set(current_primaries.keys()) - set(self._node_pubsubs.keys())
failed_nodes: list[str] = []
for node_name in new_nodes:
node = current_primaries[node_name]
pubsub = await self._ensure_node_pubsub(node)
try:
if self._subscribed_patterns:
await pubsub.psubscribe(**self._subscribed_patterns)
if self._subscribed_channels:
await pubsub.subscribe(**self._subscribed_channels)
except Exception:
# Subscription failed - remove from dict so retry is possible
await self._cleanup_node(node_name)
failed_nodes.append(node_name)
# Raise after attempting all nodes so we don't skip any
if failed_nodes:
raise ConnectionError(
f"Failed to subscribe to cluster nodes: {', '.join(failed_nodes)}"
)
async def aclose(self):
"""Close all pubsub connections and clean up resources."""
self._closed = True
for node_name in list(self._node_pubsubs.keys()):
await self._cleanup_node(node_name)
self._subscribed_patterns.clear()
self._subscribed_channels.clear()
View on GitHub (pinned to da03cdc7e8)
Solutions
- Retry refresh_subscriptions after a short backoff; the failed nodes are cleaned up so a later attempt re-subscribes them.
- Verify network reachability and ACL/credentials for the listed nodes.
- Ensure the cluster topology has stabilized (CLUSTER NODES) before retrying.
- If recurring, check the pubsub connection limits and per-node connection counts.
Example fix
// before
await notifier.refresh_subscriptions() # raises on first transient failure
// after
for attempt in range(5):
try:
await notifier.refresh_subscriptions()
break
except ConnectionError:
await asyncio.sleep(2 ** attempt)\nelse:\n raise Defensive patterns
Strategy: retry
Validate before calling
async def safe_refresh(notifier, attempts=5):
for i in range(attempts):
try:
await notifier.refresh_subscriptions()
return True
except ConnectionError:
await asyncio.sleep(2 ** i)
return False Type guard
def cluster_topology_stable(cluster) -> bool:
# heuristically, all slots assigned and no FAIL/HANDSHAKE nodes
nodes = cluster.cluster_nodes()
return all(n['flags'] and 'fail' not in n['flags'] for n in nodes) Try / catch
from redis.exceptions import ConnectionError
try:
await notifier.refresh_subscriptions()
except ConnectionError as e:
if 'Failed to subscribe' in str(e):
await asyncio.sleep(backoff)
await notifier.refresh_subscriptions() Prevention
- Wrap refresh_subscriptions in a retried loop during failovers.
- Verify reachability/ACLs on all primaries before subscribing.
- Let topology stabilize (CLUSTER NODES) before retrying.
When it happens
Trigger: Cluster topology change (failover/scale-out) adds new primary nodes; refresh_subscriptions tries to psubscribe/subscribe the new nodes' pubsub connections and the subscribe call raises (network error, node unreachable, auth failure). All failing node names are aggregated into the message.
Common situations: Cluster failover in progress where a new primary is briefly unreachable; network partition; auth/ACL mismatch on a new primary; partial outage during scaling; calling refresh_subscriptions manually right after a topology change.
Related errors
- The requested node does not exist in the cluster.
- Slot "{slot}" is not covered by the cluster.
- Failed to subscribe to cluster nodes: {', '.join(failed_node
- A {type(error).__name__} occurred while watching one or more
- Argument 'db' must be 0 or None in cluster mode
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/c5ca167f28ea36cd.json.
Report an issue: GitHub.