redis/jedis · critical · JedisClusterOperationException

Cluster retry deadline exceeded.

Error message

Cluster retry deadline exceeded.

What it means

During command execution the ClusterCommandExecutor retries failed commands against cluster nodes until an attempt/deadline budget is exhausted. When the wall-clock retry deadline passes, it throws JedisClusterOperationException("Cluster retry deadline exceeded.") carrying the last underlying exception and last node as cause/suppressed context. It means the command could not be completed within the configured timeout despite retries.

Solutions

  1. Inspect the cause/suppressed JedisConnectionException and lastNode to find the failing node; verify cluster node health with `redis-cli cluster info` / `cluster nodes`.
  2. Increase the retry deadline/maxAttempts in the cluster client configuration if your workload tolerates longer waits.
  3. Restore connectivity: restart failed nodes, fix firewall/security-group rules, and confirm slots are fully covered (no 16384-slot gaps).
  4. Reduce per-attempt timeouts so more attempts fit within the deadline instead of one slow attempt consuming it.

Example fix

// before
RedisClusterClient.create("127.0.0.1:7000")
    .build();
// after
RedisClusterClient.create("127.0.0.1:7000")
    .socketTimeout(Duration.ofSeconds(2))   // shorter per-attempt timeout
    .maxAttempts(20)                        // more retry budget
    .build();
Defensive patterns

Strategy: try-catch

Validate before calling

// verify cluster reachability before issuing commands
try (Jedis j = new Jedis(anyStartupNode)) {
  String state = j.clusterInfo();
  if (!state.contains("cluster_state:ok")) throw new IllegalStateException("cluster degraded");
}

Try / catch

try {
  return cluster.get(key);
} catch (JedisClusterOperationException e) {
  Throwable cause = e.getCause();
  logger.error("deadline exceeded on node {} : {}", e.getLastNode(), cause, e);
  throw new ServiceUnavailableException(e);
}

Prevention

When it happens

Trigger: Persistent connection failures to cluster nodes (MOVED/ASK loops, node down), Redis server unresponsive or network partitioned, while cluster-configured maxAttempts and the deadline elapse inside doExecuteCommand.

Common situations: Redis cluster node down or being failed over; network partition between client and cluster; cluster state degraded (slot not covered); severe latency making each attempt slow so the deadline expires after only a few retries.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08). Data as JSON: /api/errors/ac0341ed16bc90f7. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/redis/clients/jedis/executors/ClusterCommandExecutor.java:300

        if (followRedirections) {
          log.debug("Redirected by server to {}", jre.getTargetNode());
          redirect = jre;
          // if MOVED redirection occurred,
          if (jre instanceof JedisMovedDataException) {
            // it rebuilds cluster's slot cache recommended by Redis cluster specification
            provider.renewSlotCache(connection);
          }
        } else {
          // When followRedirections is false, throw the redirection exception immediately
          // instead of silently handling or ignoring it
          throw jre;
        }
        consecutiveConnectionFailures = 0;
      } finally {
        IOUtils.closeQuietly(connection);
      }
      if (Instant.now().isAfter(deadline)) {
        throw new JedisClusterOperationException("Cluster retry deadline exceeded.", lastException,
            lastNode);
      }
    }

    JedisClusterOperationException maxAttemptsException =
        new JedisClusterOperationException("No more cluster attempts left.", lastException,
            lastNode);
    throw maxAttemptsException;
  }

    /**
   * WARNING: This method is accessible for the purpose of testing.
   * This should not be used or overriden.
   */
  @VisibleForTesting
  protected <T> T execute(Connection connection, CommandObject<T> commandObject) {
    return connection.executeCommand(commandObject);
  }

View on GitHub (pinned to 6dac31d4c2)