elastic/elasticsearch · error · UncheckedIOException
IO error while waiting cluster
Error message
IO error while waiting cluster
What it means
Wrapped UncheckedIOException thrown from the cluster-health-yellow wait condition in ElasticsearchCluster.addWaitForClusterHealth. It fires when WaitForHttpResource.wait(500) raises an IOException while polling the cluster's HTTP endpoint for yellow health during startup.
Source
Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java:662
waitConditions.put("cluster health yellow", (node) -> {
try {
boolean httpSslEnabled = getFirstNode().isHttpSslEnabled();
WaitForHttpResource wait = new WaitForHttpResource(
httpSslEnabled ? "https" : "http",
getFirstNode().getHttpSocketURI(),
nodes.size()
);
if (httpSslEnabled) {
getFirstNode().configureHttpWait(wait);
}
List<Map<String, String>> credentials = getFirstNode().getCredentials();
if (getFirstNode().getCredentials().isEmpty() == false) {
wait.setUsername(credentials.get(0).get("useradd"));
wait.setPassword(credentials.get(0).get("-p"));
}
return wait.wait(500);
} catch (IOException e) {
throw new UncheckedIOException("IO error while waiting cluster", e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new TestClustersException("Interrupted while waiting for " + this, e);
} catch (GeneralSecurityException e) {
throw new RuntimeException("security exception", e);
}
});
}
@Nested
public NamedDomainObjectContainer<ElasticsearchNode> getNodes() {
return nodes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;View on GitHub (pinned to db6a809a66)
Solutions
- Inspect the wrapped IOException cause — connection refused vs. reset vs. SSL points to different fixes.
- Check the node's log in build/testclusters/<cluster>-<n>/logs for the crash that made HTTP unavailable.
- Verify HTTP/SSL settings consistency (httpSslEnabled must match the actual node config).
- Increase wait robustness via additional waitConditions rather than relying on a single 500ms poll.
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: confirm HTTP port is listening before relying on the wait
try (Socket s = new Socket()) {
s.connect(new InetSocketAddress(node.getHttpSocketURI().getHost(), node.getHttpSocketURI().getPort()), 1000);
} catch (IOException e) {
throw new IllegalStateException("Node HTTP port unreachable before wait", e);
} Try / catch
try {
// task that triggers cluster.start() and its wait conditions
} catch (UncheckedIOException e) {
Throwable cause = e.getCause();
// inspect cause: connection refused vs SSL vs reset
throw cause instanceof java.net.ConnectException
? new IllegalStateException("Node failed to bind HTTP — check logs", cause)
: e;
} Prevention
- Inspect node logs immediately on a wait failure (build/testclusters/<cluster>-0/logs).
- Verify httpSslEnabled matches the node's SSL configuration.
- Add robust wait conditions rather than relying on a single 500ms poll.
When it happens
Trigger: The wait condition fails with IOException — the node's HTTP port is unreachable, returns malformed responses, or the connection is reset during the health poll. Original IOException is the cause.
Common situations: The node process crashed during boot; HTTP/SSL misconfiguration; the port is bound but the REST layer is not ready; firewall/proxy interference in CI; transient network blips against the loopback wait endpoint.
Related errors
- Failed to write unicast_hosts for {}
- Interrupted while waiting for {}
- security exception
- Number of nodes should be >= 1 but was {} for {}
- Cannot shrink {} to have {} nodes as it already has {}
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/d39a1575c50f5097.
Report an issue: GitHub.