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

  1. Inspect the wrapped IOException cause — connection refused vs. reset vs. SSL points to different fixes.
  2. Check the node's log in build/testclusters/<cluster>-<n>/logs for the crash that made HTTP unavailable.
  3. Verify HTTP/SSL settings consistency (httpSslEnabled must match the actual node config).
  4. 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

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


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/d39a1575c50f5097. Report an issue: GitHub.