{"id":"d71ad6a981a57c95","repo":"redis/redis-py","slug":"all-slots-are-not-covered-after-query-all-startup","errorCode":null,"errorMessage":"All slots are not covered after query all startup_nodes. {len(tmp_slots)} of {REDIS_CLUSTER_HASH_SLOTS} covered...","messagePattern":"All slots are not covered after query all startup_nodes\\. (.+?) of (.+?) covered\\.\\.\\.","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"critical","filePath":"redis/asyncio/cluster.py","lineNumber":2380,"sourceCode":"                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\n\n            # Check if the slots are not fully covered\n            if not fully_covered and self.require_full_coverage:\n                # Despite the requirement that the slots be covered, there\n                # isn't a full coverage\n                raise RedisClusterException(\n                    f\"All slots are not covered after query all startup_nodes. \"\n                    f\"{len(tmp_slots)} of {REDIS_CLUSTER_HASH_SLOTS} \"\n                    f\"covered...\"\n                )\n\n            # Set the tmp variables to the real variables\n            self.set_nodes(self.nodes_cache, tmp_nodes_cache, remove_old=True)\n            # tmp_slots was built from CLUSTER SLOTS responses and can contain\n            # newly-created ClusterNode objects for nodes we already know about.\n            # Rebuild the slots cache with the preserved nodes_cache instances\n            # so existing per-node connection pools stay in use after refresh.\n            # Keep the shared node-list-per-slot-range shape from tmp_slots to\n            # avoid allocating a separate list for every slot.\n            node_lists_by_id: Dict[int, List[\"ClusterNode\"]] = {}\n            new_slots_cache: Dict[int, List[\"ClusterNode\"]] = {}\n            for slot, nodes in tmp_slots.items():\n                node_list_id = id(nodes)\n                slot_nodes = node_lists_by_id.get(node_list_id)","sourceCodeStart":2362,"sourceCodeEnd":2398,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/cluster.py#L2362-L2398","documentation":"Raised after initialize() queried all reachable startup nodes and the merged slots cache still does not cover all REDIS_CLUSTER_HASH_SLOTS (16384), but only when require_full_coverage=True (the default). The client refuses to operate with partial coverage because some keys' slots would have no owning node, causing unpredictable routing failures later.","triggerScenarios":"Connecting to a cluster that is missing slots (degraded, partially failed, or still being created); a cluster where some nodes are unreachable so their slots aren't represented; require_full_coverage=True (default) with an incomplete topology.","commonSituations":"A node owning a slot range is down during client startup; cluster creation not yet finished (slots not all assigned); flaky seed nodes returning partial CLUSTER SLOTS; connecting during maintenance/failover.","solutions":["Ensure all nodes (especially primaries) are up and reachable, then retry client creation.","If partial coverage is acceptable for your workload, construct with require_full_coverage=False (note: uncovered slots will then raise SlotNotCoveredError at runtime).","Wait for the cluster to report cluster_state:ok (redis-cli CLUSTER INFO) before connecting."],"exampleFix":"// before\nrc = RedisCluster(host='seed', port=6379)  # partial coverage, default True\n\n// after\nrc = RedisCluster(host='seed', port=6379, require_full_coverage=False)","handlingStrategy":"validation","validationCode":"import redis.asyncio as aioredis\n\nasync def cluster_slot_coverage(host, port) -> tuple[int, int]:\n    r = aioredis.Redis(host=host, port=port)\n    try:\n        slots = await r.cluster_slots()\n        covered = sum((s[1] - s[0] + 1) for s in slots)\n        return covered, 16384\n    finally:\n        await r.aclose()\n\ncovered, total = await cluster_slot_coverage(host, port)\nif covered < total:\n    raise RuntimeError(f'Cluster only covers {covered}/{total} slots')","typeGuard":"def full_coverage(covered: int, total: int = 16384) -> bool:\n    return covered == total","tryCatchPattern":"from redis.cluster import RedisClusterException\n\ntry:\n    rc = RedisCluster(host=h, port=p)\nexcept RedisClusterException as e:\n    if 'not covered' in str(e):\n        # either wait for the cluster to heal, or allow partial coverage\n        rc = RedisCluster(host=h, port=p, require_full_coverage=False)\n    else:\n        raise","preventionTips":["Confirm CLUSTER INFO reports cluster_state:ok and all 16384 slots assigned before connecting.","Decide deliberately whether require_full_coverage=False is acceptable for your workload.","Add a startup health check that retries until coverage is complete."],"tags":["redis-cluster","startup","slot-coverage","degraded-cluster"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}