{"record":{"id":"2f35cc87756dfb37","repo":"redis/redis-py","slug":"startup-nodes-could-not-agree-on-a-valid-slots-cac","errorCode":null,"errorMessage":"startup_nodes could not agree on a valid slots cache: {', '.join(disagreements)}","messagePattern":"startup_nodes could not agree on a valid slots cache: (.+?)","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/asyncio/cluster.py","lineNumber":2357,"sourceCode":"                            )\n                        # add this node to the nodes cache\n                        tmp_nodes_cache[target_replica_node.name] = target_replica_node\n                        nodes_for_slot.append(target_replica_node)\n\n                    for i in range(int(slot[0]), int(slot[1]) + 1):\n                        if i not in tmp_slots:\n                            tmp_slots[i] = nodes_for_slot\n                        else:\n                            # Validate that 2 nodes want to use the same slot cache\n                            # setup\n                            tmp_slot = tmp_slots[i][0]\n                            if tmp_slot.name != target_node.name:\n                                disagreements.append(\n                                    f\"{tmp_slot.name} vs {target_node.name} on slot: {i}\"\n                                )\n\n                                if len(disagreements) > 5:\n                                    raise RedisClusterException(\n                                        f\"startup_nodes could not agree on a valid \"\n                                        f\"slots cache: {', '.join(disagreements)}\"\n                                    )\n\n                # Validate if all slots are covered or if we should try next startup node\n                fully_covered = True\n                for i in range(REDIS_CLUSTER_HASH_SLOTS):\n                    if i not in tmp_slots:\n                        fully_covered = False\n                        break\n                if fully_covered:\n                    break\n\n            if not startup_nodes_reachable:\n                raise RedisClusterException(\n                    f\"Redis Cluster cannot be connected. Please provide at least \"\n                    f\"one reachable node: {str(exception)}\"\n                ) from exception","sourceCodeStart":2339,"sourceCodeEnd":2375,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/cluster.py#L2339-L2375","documentation":"Raised while building tmp_slots during initialize() when more than 5 slot-range disagreements are detected between the responses of different startup nodes (two nodes claim ownership of the same slot for different primaries). This indicates conflicting cluster views, so the client aborts rather than picking one arbitrarily.","triggerScenarios":"Two or more startup nodes disagree on which node owns a slot, accumulating >5 conflicts before the full-coverage check. Typical during a botched reshard, a split-brain, or when startup_nodes inadvertently mix nodes from two different clusters.","commonSituations":"Mixing endpoints from different Redis Cluster deployments in startup_nodes; mid-reshard race where slot ownership is being transferred; nodes running incompatible cluster configs.","solutions":["Verify all startup_nodes belong to the same cluster (same cluster announce / node IDs via redis-cli CLUSTER NODES).","Retry initialize() after the reshard completes; transient disagreements resolve once ownership stabilises.","Reduce startup_nodes to the cluster's known-good seed nodes only.","If persistent, inspect CLUSTER NODES on each node to find the disagreement and fix the broken slot assignment."],"exampleFix":"// before\nrc = RedisCluster(startup_nodes=[\n    ClusterNode('cluster-a', 7000),\n    ClusterNode('cluster-b', 7000),  # different cluster!\n])\n\n// after\nrc = RedisCluster(startup_nodes=[\n    ClusterNode('cluster-a', 7000),\n    ClusterNode('cluster-a', 7001),\n])","handlingStrategy":"validation","validationCode":"async def assert_same_cluster(startup_nodes):\n    # compare cluster node ids across all seed nodes\n    from redis.asyncio import Redis\n    node_ids = set()\n    for host, port in startup_nodes:\n        r = Redis(host=host, port=port)\n        try:\n            nid = (await r.execute_command('CLUSTER', 'MYID'))\n            node_ids.add(nid)\n        finally:\n            await r.aclose()\n    # all seeds should reference one cluster; not identical ids but overlapping membership\n    return node_ids","typeGuard":"null","tryCatchPattern":"from redis.exceptions import RedisClusterException\ntry:\n    await rc.initialize()\nexcept RedisClusterException as e:\n    if 'could not agree' in str(e):\n        # trim startup_nodes to known-good seeds and retry\n        rc.startup_nodes = {n.name: n for n in rc.startup_nodes.values()[:1]}\n        await rc.initialize()","preventionTips":["Only include startup_nodes from the same cluster.","Avoid mixing endpoints from different Redis Cluster deployments.","Retry initialize() transiently during resharding; fix persistent disagreements via CLUSTER NODES."],"tags":["cluster","topology","slots","split-brain"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}