{"record":{"id":"66df4d6e8ab3a959","repo":"redisson/redisson","slug":"unable-to-find-master-node-master","errorCode":null,"errorMessage":"Unable to find master node: ${master}","messagePattern":"Unable to find master node: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"redisson-spring/redisson-spring-data/redisson-spring-data-20/src/main/java/org/redisson/spring/data/connection/RedissonClusterConnection.java","lineNumber":85,"sourceCode":"                new ObjectDecoder(new RedisClusterNodeDecoder(executorService.getServiceManager())));\n        return read(null, StringCodec.INSTANCE, cluster);\n    }\n\n    @Override\n    public Collection<RedisClusterNode> clusterGetSlaves(RedisClusterNode master) {\n        Iterable<RedisClusterNode> res = clusterGetNodes();\n        RedisClusterNode masterNode = null;\n        for (Iterator<RedisClusterNode> iterator = res.iterator(); iterator.hasNext();) {\n            RedisClusterNode redisClusterNode = iterator.next();\n            if (master.getHost().equals(redisClusterNode.getHost()) \n                    && master.getPort().equals(redisClusterNode.getPort())) {\n                masterNode = redisClusterNode;\n                break;\n            }\n        }\n        \n        if (masterNode == null) {\n            throw new IllegalStateException(\"Unable to find master node: \" + master);\n        }\n        \n        for (Iterator<RedisClusterNode> iterator = res.iterator(); iterator.hasNext();) {\n            RedisClusterNode redisClusterNode = iterator.next();\n            if (redisClusterNode.getMasterId() == null \n                    || !redisClusterNode.getMasterId().equals(masterNode.getId())) {\n                iterator.remove();\n            }\n        }\n        return (Collection<RedisClusterNode>) res;\n    }\n\n    @Override\n    public Map<RedisClusterNode, Collection<RedisClusterNode>> clusterGetMasterSlaveMap() {\n        Iterable<RedisClusterNode> res = clusterGetNodes();\n        \n        Set<RedisClusterNode> masters = new HashSet<RedisClusterNode>();\n        for (Iterator<RedisClusterNode> iterator = res.iterator(); iterator.hasNext();) {","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/redisson/redisson/blob/91188987c2a9023e7fedabff758681ad9b107f25/redisson-spring/redisson-spring-data/redisson-spring-data-20/src/main/java/org/redisson/spring/data/connection/RedissonClusterConnection.java#L67-L103","documentation":"RedissonClusterConnection.getSlaveConnection/getMasterConnection-style node resolution (method at RedissonClusterConnection.java:85, redisson-spring-data-20) throws IllegalStateException('Unable to find master node: <node>') when the node requested by the caller is not present in the topology returned by clusterGetNodes(). The lookup matches host AND port, so any host/port/id mismatch (e.g. after failover or resharding, or when nodes report internal IPs) makes masterNode null.","triggerScenarios":"Passing a RedisClusterNode to a node-specific operation (e.g. getClientList(node), node-specific reads/writes) whose host:port does not exactly equal any node in CLUSTER NODES output — typically because the caller cached a stale node view, the cluster failed over, or NAT/Docker maps advertised ports differently.","commonSituations":"Redis Cluster in Docker/K8s with NAT where nodes advertise internal IPs (announcedIP missing); a cached RedisClusterNode from a previous topology used after failover; comparing a node built from config (e.g. 127.0.0.1:7000) with the topology entry (10.0.0.5:7000); slot migration in progress.","solutions":["Always obtain RedisClusterNode instances from clusterGetNodes()/clusterGetSlots() at call time instead of caching or constructing them manually","Refresh the node view (clusterGetNodes) and retry once when this exception occurs","For NAT/Docker deployments configure cluster-announce-ip / cluster-announce-port so advertised host:port match what the client sees","Match by node Id when possible rather than host:port, since Ids are stable across DNS changes"],"exampleFix":"// before\nRedisClusterNode stale = cachedNode; // captured minutes ago\nconnection.getClientList(stale); // may throw after failover\n\n// after\nRedisClusterNode master = connection.clusterGetNodes().stream()\n    .filter(n -> n.getId().equals(wantedId))\n    .findFirst().orElseThrow();\nconnection.getClientList(master);","handlingStrategy":"fallback","validationCode":"// Resolve nodes fresh and match by stable Id before node-specific calls\nCollection<RedisClusterNode> nodes = clusterConnection.clusterGetNodes();\nOptional<RedisClusterNode> target = nodes.stream()\n    .filter(n -> n.getId().equals(wantedId))\n    .findFirst();\nif (!target.isPresent()) {\n    nodes = clusterConnection.clusterGetNodes(); // refresh topology once\n    target = nodes.stream().filter(n -> n.getId().equals(wantedId)).findFirst();\n}\n// proceed only if target.isPresent()","typeGuard":"Optional<RedisClusterNode> resolveNode(RedisClusterConnection c, String nodeId) {\n    return c.clusterGetNodes().stream()\n        .filter(n -> n.getId().equals(nodeId)).findFirst();\n}","tryCatchPattern":"catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Unable to find master node\")) {\n        // refresh clusterGetNodes() and retry once; else surface topology-change alert\n    } else throw e;\n}","preventionTips":["Never cache RedisClusterNode across requests; re-resolve each time","Prefer node Id over host:port for lookups","Configure cluster-announce-ip/port in NAT/Docker clusters","Retry once on topology errors to ride through failovers"],"tags":["redis","redisson","cluster","failover","topology","nat"],"backgroundTag":null,"analyzedSha":"91188987c2a9023e7fedabff758681ad9b107f25","analyzedAt":"2026-08-14T11:39:42.619Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}