redisson/redisson · error · IllegalStateException

Unable to find master node:

Error message

Unable to find master node: 

What it means

RedissonClusterConnection.clusterGetReplicas (called with a master) fetches the cluster node list via CLUSTER NODES and searches it for a node whose host and port match the given master. If no advertised node matches, it throws IllegalStateException('Unable to find master node: ' + master). The comparison is exact string equality on host and port, so mismatches arise when the cluster advertises different host identifiers (IP vs hostname, NAT/Docker addresses) than the node object the caller supplied.

Source

Thrown at redisson-spring/redisson-spring-data/redisson-spring-data-17/src/main/java/org/redisson/spring/data/connection/RedissonClusterConnection.java:81

                new ObjectDecoder(new RedisClusterNodeDecoder(executorService.getServiceManager())));
        return read(null, StringCodec.INSTANCE, cluster);
    }

    @Override
    public Collection<RedisClusterNode> clusterGetSlaves(RedisClusterNode master) {
        Iterable<RedisClusterNode> res = clusterGetNodes();
        RedisClusterNode masterNode = null;
        for (Iterator<RedisClusterNode> iterator = res.iterator(); iterator.hasNext();) {
            RedisClusterNode redisClusterNode = iterator.next();
            if (master.getHost().equals(redisClusterNode.getHost()) 
                    && master.getPort().equals(redisClusterNode.getPort())) {
                masterNode = redisClusterNode;
                break;
            }
        }
        
        if (masterNode == null) {
            throw new IllegalStateException("Unable to find master node: " + master);
        }
        
        for (Iterator<RedisClusterNode> iterator = res.iterator(); iterator.hasNext();) {
            RedisClusterNode redisClusterNode = iterator.next();
            if (redisClusterNode.getMasterId() == null 
                    || !redisClusterNode.getMasterId().equals(masterNode.getId())) {
                iterator.remove();
            }
        }
        return (Collection<RedisClusterNode>) res;
    }

    @Override
    public Map<RedisClusterNode, Collection<RedisClusterNode>> clusterGetMasterSlaveMap() {
        Iterable<RedisClusterNode> res = clusterGetNodes();
        
        Set<RedisClusterNode> masters = new HashSet<RedisClusterNode>();
        for (Iterator<RedisClusterNode> iterator = res.iterator(); iterator.hasNext();) {

View on GitHub (pinned to 91188987c2)

Solutions

  1. Pass a RedisClusterNode obtained from clusterGetNodes() itself rather than constructing one manually — its host/port come from the cluster's own advertisement
  2. Fix advertised addresses on the Redis cluster (cluster-announce-ip / cluster-announce-port in redis.conf or Docker command) so the topology the app sees is connectable and consistent
  3. Refresh topology (re-invoke clusterGetNodes) instead of reusing node objects across failovers/reshards

Example fix

// before
RedisClusterNode master = new RedisClusterNode("redis.example.com", 6379); // hostname
connection.clusterGetReplicas(master); // cluster advertises 10.0.0.5:6379 -> throws

// after
Iterable<RedisClusterNode> nodes = connection.clusterGetNodes();
RedisClusterNode master = StreamSupport.stream(nodes.spliterator(), false)
        .filter(RedisClusterNode::isMaster)
        .filter(n -> n.getHost().equals("10.0.0.5"))
        .findFirst().orElseThrow();
connection.clusterGetReplicas(master);
Defensive patterns

Strategy: validation

Validate before calling

// always source nodes from the cluster itself
RedisClusterNode match = StreamSupport.stream(connection.clusterGetNodes().spliterator(), false)
        .filter(n -> n.getHost().equals(master.getHost()) && n.getPort() == master.getPort())
        .findFirst().orElseThrow(() ->
            new IllegalStateException("master " + master + " not in current topology; refresh clusterGetNodes"));
connection.clusterGetReplicas(match);

Type guard

boolean nodeInTopology(RedisClusterNode node, Iterable<RedisClusterNode> topology) {
    return StreamSupport.stream(topology.spliterator(), false)
        .anyMatch(n -> n.getHost().equals(node.getHost())
                    && n.getPort() == node.getPort());
}

Try / catch

try {
    return connection.clusterGetReplicas(master);
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Unable to find master node")) {
        Iterable<RedisClusterNode> fresh = connection.clusterGetNodes();
        // retry with a node taken from the refreshed topology
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling clusterGetReplicas / RedisTemplate opsForCluster().replicas(node) with a RedisClusterNode whose host/port do not literally equal any host:port in CLUSTER NODES output — typically because the caller built the node from a config hostname while the cluster advertises IPs (or vice versa), or behind NAT/container port mappings.

Common situations: Docker/Kubernetes deployments where Redis advertises internal IPs but the app resolves external hostnames; obtaining the node from a previous, stale cluster topology after a failover or reshard; passing a manually constructed RedisClusterNode instead of one returned by clusterGetNodes(); multi-NAT environments rewriting advertised ports.

Related errors


AI-assisted analysis of redisson/redisson@91188987c2 (2026-08-14). Data as JSON: /api/errors/1da24723581937d0. Report an issue: GitHub.