redis/redis-py · critical · RedisClusterException

All slots are not covered after query all startup_nodes.

Error message

All slots are not covered after query all startup_nodes. {len(tmp_slots)} of {REDIS_CLUSTER_HASH_SLOTS} covered...

What it means

Raised after initialize() contacts at least one node but the merged CLUSTER SLOTS responses still do not cover all 16384 slots, while require_full_coverage=True. The client is configured to refuse partial coverage, so it aborts startup with the count of covered slots.

Solutions

  1. Restore the downed primary or trigger manual failover so every slot is served.
  2. Recreate/finish the cluster (redis-cli --cluster create / fix) so all slots are assigned.
  3. If you can tolerate partial unavailability, construct the client with require_full_coverage=False.
  4. Retry initialize() after cluster health is restored; the covered-slot count should reach 16384.

Example fix

// before
rc = RedisCluster(host='localhost', port=7000)  # partial cluster -> raises

// after
# fix cluster: redis-cli --cluster fix 127.0.0.1:7000
# or tolerate partial coverage:
rc = RedisCluster(host='localhost', port=7000, require_full_coverage=False)
Defensive patterns

Strategy: fallback

Validate before calling

async def covered_slots(host, port):
    from redis.asyncio import Redis
    r = Redis(host=host, port=port)
    try:
        slots = await r.execute_command('CLUSTER', 'SLOTS')
        covered = sum(int(s[1]) - int(s[0]) + 1 for s in slots)
        return covered
    finally:
        await r.aclose()

if await covered_slots(host, port) < 16384:
    rc = RedisCluster(host=host, port=port, require_full_coverage=False)
else:
    rc = RedisCluster(host=host, port=port)

Type guard

null

Try / catch

from redis.exceptions import RedisClusterException
try:
    rc = RedisCluster(host=host, port=port)
    await rc.initialize()
except RedisClusterException as e:
    if 'All slots are not covered' in str(e):
        rc = RedisCluster(host=host, port=port, require_full_coverage=False)
        await rc.initialize()

Prevention

When it happens

Trigger: At least one startup node answered, but some slot ranges have no owner (unreachable primaries with no replicas, cluster still being created, or nodes that failed mid-build). require_full_coverage defaults True for the async cluster client.

Common situations: Cluster that has lost primaries for some slots without failing over; brand-new cluster not fully created (CLUSTER CREATE incomplete); subset of nodes down at client startup.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/d71ad6a981a57c95. Report an issue: GitHub.

Appendix: source

Thrown at redis/asyncio/cluster.py:2381

                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

            # Check if the slots are not fully covered
            if not fully_covered and self.require_full_coverage:
                # Despite the requirement that the slots be covered, there
                # isn't a full coverage
                raise RedisClusterException(
                    f"All slots are not covered after query all startup_nodes. "
                    f"{len(tmp_slots)} of {REDIS_CLUSTER_HASH_SLOTS} "
                    f"covered..."
                )

            # Set the tmp variables to the real variables
            self.set_nodes(self.nodes_cache, tmp_nodes_cache, remove_old=True)
            # tmp_slots was built from CLUSTER SLOTS responses and can contain
            # newly-created ClusterNode objects for nodes we already know about.
            # Rebuild the slots cache with the preserved nodes_cache instances
            # so existing per-node connection pools stay in use after refresh.
            # Keep the shared node-list-per-slot-range shape from tmp_slots to
            # avoid allocating a separate list for every slot.
            node_lists_by_id: Dict[int, List["ClusterNode"]] = {}
            new_slots_cache: Dict[int, List["ClusterNode"]] = {}
            for slot, nodes in tmp_slots.items():
                node_list_id = id(nodes)
                slot_nodes = node_lists_by_id.get(node_list_id)

View on GitHub (pinned to 6a6b581b48)