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
- Restore the downed primary or trigger manual failover so every slot is served.
- Recreate/finish the cluster (redis-cli --cluster create / fix) so all slots are assigned.
- If you can tolerate partial unavailability, construct the client with require_full_coverage=False.
- 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
- Restore failed primaries or run redis-cli --cluster fix so all slots are owned.
- If partial coverage is acceptable for your workload, set require_full_coverage=False explicitly.
- Monitor slot coverage as part of cluster health checks.
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
- Argument 'db' must be 0 or None in cluster mode
- Cluster mode is not enabled on this node
- Redis Cluster cannot be connected. Please provide at least…
- RedisCluster requires at least one node to discover the…
- Slot " " is not covered by the cluster.
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)