redis/redis-py · critical · RedisClusterException

startup_nodes could not agree on a valid slots cache: {', '.

Error message

startup_nodes could not agree on a valid slots cache: {', '.join(disagreements)}

What it means

Raised during initialize() when more than 5 slot-ownership disagreements accumulate across startup nodes. As the client merges CLUSTER SLOTS replies from multiple nodes, if two different nodes claim ownership of the same slot (line 2350 check), the disagreement is recorded; exceeding the threshold aborts bootstrapping because the topology is inconsistent and cannot be safely resolved.

Source

Thrown at redis/asyncio/cluster.py:2356

                            )
                        # add this node to the nodes cache
                        tmp_nodes_cache[target_replica_node.name] = target_replica_node
                        nodes_for_slot.append(target_replica_node)

                    for i in range(int(slot[0]), int(slot[1]) + 1):
                        if i not in tmp_slots:
                            tmp_slots[i] = nodes_for_slot
                        else:
                            # Validate that 2 nodes want to use the same slot cache
                            # setup
                            tmp_slot = tmp_slots[i][0]
                            if tmp_slot.name != target_node.name:
                                disagreements.append(
                                    f"{tmp_slot.name} vs {target_node.name} on slot: {i}"
                                )

                                if len(disagreements) > 5:
                                    raise RedisClusterException(
                                        f"startup_nodes could not agree on a valid "
                                        f"slots cache: {', '.join(disagreements)}"
                                    )

                # Validate if all slots are covered or if we should try next startup node
                fully_covered = True
                for i in range(REDIS_CLUSTER_HASH_SLOTS):
                    if i not in tmp_slots:
                        fully_covered = False
                        break
                if fully_covered:
                    break

            if not startup_nodes_reachable:
                raise RedisClusterException(
                    f"Redis Cluster cannot be connected. Please provide at least "
                    f"one reachable node: {str(exception)}"
                ) from exception

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Retry client creation after resharding/failover completes (await asyncio.sleep then reconstruct RedisCluster).
  2. Reduce startup_nodes to a single known-good seed node so only one CLUSTER SLOTS view is trusted.
  3. Ensure the cluster is healthy (CLUSTER NODES shows no fail/pfail state) before the client connects.

Example fix

// before
rc = RedisCluster(startup_nodes=[nodeA, nodeB, nodeC])  # during reshard

// after
# wait for reshard to finish, then seed from one node
rc = RedisCluster(host='seed-host', port=6379)
Defensive patterns

Strategy: retry

Validate before calling

import asyncio
from redis.asyncio.cluster import RedisCluster

async def connect_stable(startup_nodes, max_retries=3):
    for attempt in range(max_retries):
        try:
            return await RedisCluster(startup_nodes=startup_nodes)
        except Exception as e:
            if 'could not agree' in str(e):
                await asyncio.sleep(2 ** attempt)
                continue
            raise
    raise RuntimeError('cluster topology still unstable')

Type guard

def cluster_looks_healthy(cluster_nodes_text: str) -> bool:
    lines = [l for l in cluster_nodes_text.splitlines() if l and not l.startswith('node')]
    return all('fail' not in l.split()[-2].lower() for l in lines if len(l.split()) >= 8)

Try / catch

from redis.cluster import RedisClusterException

try:
    rc = RedisCluster(startup_nodes=seeds)
except RedisClusterException as e:
    if 'could not agree' in str(e):
        # wait for reshard/failover to settle and seed from one node
        await asyncio.sleep(5)
        rc = RedisCluster(host=seeds[0].host, port=seeds[0].port)
    else:
        raise

Prevention

When it happens

Trigger: Bootstrapping against a cluster mid-reshard where slots are being migrated and nodes disagree transiently; connecting during an unstable split-brain; nodes running mismatched cluster configs; a partially-failed failover leaving conflicting slot claims.

Common situations: Connecting during active slot migration/rebalancing; a flaky network partition where nodes' CLUSTER SLOTS are out of sync; multiple startup_nodes pointing at different sub-clusters.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/2f35cc87756dfb37.json. Report an issue: GitHub.