{"id":"bddd87fbd707983d","repo":"redis/redis-py","slug":"failed-to-subscribe-to-cluster-nodes-join-f-bddd87","errorCode":null,"errorMessage":"Failed to subscribe to cluster nodes: {', '.join(failed_nodes)}","messagePattern":"Failed to subscribe to cluster nodes: (.+?)","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"redis/keyspace_notifications.py","lineNumber":2164,"sourceCode":"            new_nodes = set(current_primaries.keys()) - set(self._node_pubsubs.keys())\n            failed_nodes: list[str] = []\n            for node_name in new_nodes:\n                node = current_primaries[node_name]\n                pubsub = self._ensure_node_pubsub(node)\n\n                try:\n                    if self._subscribed_patterns:\n                        pubsub.psubscribe(**self._subscribed_patterns)\n                    if self._subscribed_channels:\n                        pubsub.subscribe(**self._subscribed_channels)\n                except Exception:\n                    # Subscription failed - remove from dict so retry is possible\n                    self._cleanup_node(node_name)\n                    failed_nodes.append(node_name)\n\n            # Raise after attempting all nodes so we don't skip any\n            if failed_nodes:\n                raise ConnectionError(\n                    f\"Failed to subscribe to cluster nodes: {', '.join(failed_nodes)}\"\n                )\n\n    def close(self):\n        \"\"\"Close all pubsub connections and clean up resources.\"\"\"\n        self._closed = True\n        for node_name in list(self._node_pubsubs.keys()):\n            self._cleanup_node(node_name)\n        self._subscribed_patterns.clear()\n        self._subscribed_channels.clear()\n","sourceCodeStart":2146,"sourceCodeEnd":2175,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/keyspace_notifications.py#L2146-L2175","documentation":"Raised as redis.exceptions.ConnectionError by ClusterKeyspaceNotifications.refresh_subscriptions (redis/keyspace_notifications.py:2164). After a cluster topology change (or a broken pubsub connection) the manager re-subscribes every new primary to the registered patterns/channels; if one or more nodes fail to subscribe, they are cleaned up and the list of failed node names is aggregated into a single error raised after all nodes are attempted.","triggerScenarios":"A cluster topology refresh (failover, resharding, node add/remove) or a dropped pubsub connection that triggers refresh_subscriptions(), where subscribe()/psubscribe() on at least one primary node raises. Called automatically on connection errors and topology changes, or manually.","commonSituations":"A primary node is temporarily unreachable during failover; network partition isolating some nodes; overloaded node rejecting pubsub subscriptions; ACL denying SUBSCRIBE on a node; nodes restarting mid-refresh.","solutions":["Catch ConnectionError and retry — refresh_subscriptions is idempotent and will re-attempt the failed nodes on the next call.","Verify cluster health (CLUSTER NODES) and that all primaries are reachable before relying on notifications.","Ensure the ACL/user used by the client has SUBSCRIBE/PSUBSCRIBE permission on every primary.","Check network connectivity/firewall between the client and each primary node listed in the error.","If a node is permanently down, remove it from the cluster or wait for failover to promote a reachable replica."],"exampleFix":"// before\nckn.refresh_subscriptions()  # may raise mid-failover\n\n// after\nimport time\nfrom redis.exceptions import ConnectionError\nfor _ in range(5):\n    try:\n        ckn.refresh_subscriptions()\n        break\n    except ConnectionError:\n        time.sleep(1)  # backoff; next call re-attempts failed nodes","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"import time\nfrom redis.exceptions import ConnectionError\nfor attempt in range(max_refresh_attempts):\n    try:\n        ckn.refresh_subscriptions()\n        break\n    except ConnectionError as e:\n        # failed node names are in the message; will be re-attempted next call\n        time.sleep(backoff_for(attempt))\nelse:\n    raise","preventionTips":["Treat refresh_subscriptions failures as transient — the method is idempotent and re-attempts failed nodes.","Verify all primaries are reachable (CLUSTER NODES) and the ACL grants SUBSCRIBE/PSUBSCRIBE on each.","Check firewall/network paths to every primary listed in the error.","During failover, allow time for a replica to be promoted before re-subscribing."],"tags":["keyspace-notifications","cluster","failover","network","connectionerror","topology"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}