redis/jedis · error · JedisValidationException

Timeout while waiting for health check result

Error message

Timeout while waiting for health check result

What it means

StatusTracker.waitForHealthStatus blocks on a CountDownLatch waiting for the health status manager to report a state change for a MultiDb failover endpoint. If the latch does not count down within healthStatusManager.getMaxWaitFor(endpoint) milliseconds, the tracker gives up and throws JedisValidationException so callers never block indefinitely on an unreachable or unhealthy endpoint.

Solutions

  1. Verify the target endpoint is reachable and healthy (redis-cli -h host -p port PING) before client initialization.
  2. Increase the configured max wait for the endpoint so slow health checks can complete.
  3. Check that the health check probe configuration (interval, timeout, endpoint address) matches the actual Redis instance.
  4. Inspect network/firewall/security-group rules between client and endpoint if the latch times out consistently.
  5. Catch JedisValidationException around initialization and fall back to another endpoint in the multi-db list.

Example fix

// before
MultiDbClient client = MultiDbClient.builder()
    .multiDbEndpoints(endpoints) // endpoint health check max wait too small
    .build();
// after
MultiDbClient client = MultiDbClient.builder()
    .multiDbEndpoints(endpoints)
    .connectionTimeout(5, TimeUnit.SECONDS) // give health checks room to complete
    .healthCheckMaxWait(30, TimeUnit.SECONDS)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

if (!isEndpointReachable(endpoint)) throw new IllegalStateException("skip init: endpoint down " + endpoint);
// and ensure configured maxWait >= worst-case connect+health-check time

Try / catch

try {
  tracker.waitForHealthStatus(endpoint);
} catch (JedisValidationException e) {
  log.warn("health check timed out for {}", endpoint, e);
  failoverToAlternateEndpoint();
}

Prevention

When it happens

Trigger: Calling waitForHealthStatus (typically via waitForInitializationPolicy during MultiDbClient/RedisClient failover-aware initialization) when the endpoint's health check never completes within the configured max wait window, e.g. the Redis node is unreachable, the health check listener never fires, or the max-wait timeout is set too low for slow networks or slow Redis startup.

Common situations: Configuring a multi-db client against a dead or firewalled Redis endpoint; setting the per-endpoint max wait (healthCheckMaxWait / getMaxWaitFor source) to a value smaller than actual connection establishment time; Redis starting slower than the client during orchestrated failovers or container startup races.

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/396b4c44f9c1bd54. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/redis/clients/jedis/mcf/StatusTracker.java:68

      }
    };

    // Register the temporary listener
    healthStatusManager.registerListener(endpoint, tempListener);

    try {
      // Double-check status after registering listener (race condition protection)
      currentStatus = healthStatusManager.getHealthStatus(endpoint);
      if (currentStatus != HealthStatus.UNKNOWN) {
        return currentStatus;
      }

      // Wait for the health status change event
      // just for safety to not block indefinitely
      boolean completed = latch.await(healthStatusManager.getMaxWaitFor(endpoint),
        TimeUnit.MILLISECONDS);
      if (!completed) {
        throw new JedisValidationException("Timeout while waiting for health check result");
      }
      return resultStatus.get();

    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new JedisConnectionException("Interrupted while waiting for health check result", e);
    } finally {
      // Clean up: unregister the temporary listener
      healthStatusManager.unregisterListener(endpoint, tempListener);
    }
  }
}

View on GitHub (pinned to 6dac31d4c2)