elastic/elasticsearch · error · GradleException

Unable to start APM server: {}

Error message

Unable to start APM server: {}

What it means

Thrown by RunTask when MockApmServer.start() raises an IOException while apmServerEnabled is true. The exception is rethrown as a GradleException with the original IOException's message and cause. Note: only e.getMessage() is included — if the IOException has no message the string is empty/null and the cause carries the real detail.

Source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java:285

        if (nodeCount != null) {
            for (ElasticsearchCluster cluster : getClusters()) {
                cluster.setNumberOfNodes(nodeCount);
            }
        }
        boolean singleNode = getClusters().stream().mapToLong(c -> c.getNodes().size()).sum() == 1;
        final Function<ElasticsearchNode, Path> getDataPath;
        if (singleNode) {
            getDataPath = n -> dataDir;
        } else {
            getDataPath = n -> dataDir.resolve(n.getName());
        }

        if (apmServerEnabled) {
            try {
                mockServer = new MockApmServer(apmServerMetrics, apmServerTransactions, apmServerTransactionsExcludes);
                mockServer.start();
            } catch (IOException e) {
                throw new GradleException("Unable to start APM server: " + e.getMessage(), e);
            }
        }

        for (ElasticsearchCluster cluster : getClusters()) {
            cluster.setPreserveDataDir(preserveData);
            for (ElasticsearchNode node : cluster.getNodes()) {
                node.setHttpPort(String.valueOf(httpPort++));
                node.setTransportPort(String.valueOf(transportPort++));
                additionalSettings.forEach(node::setting);
                if (dataDir != null) {
                    node.setDataPath(getDataPath.apply(node));
                }
                if (keystorePassword.length() > 0) {
                    node.keystorePassword(keystorePassword);
                }
                if (useHttps) {
                    validateHelperOption("--https", "xpack.security.http.ssl", node);
                    node.setting("xpack.security.http.ssl.enabled", "true");

View on GitHub (pinned to db6a809a66)

Solutions

  1. Read the wrapped IOException's cause for the bind/grpc failure detail (the top-level message may be empty).
  2. If ephemeral ports are exhausted, reduce concurrent testclusters / raise net.ipv4.ip_local_port_range.
  3. Disable gRPC native transport if the failure is native-lib loading: set -Dio.grpc.netty.useNativeTransport=false in the gradle JVM.
  4. If you do not need APM, set apmServerEnabled = false on the RunTask to bypass this code path entirely.

Example fix

// before:
runTask.apmServerEnabled = true  // fails on this host
// after (if APM unused):
runTask.apmServerEnabled = false
Defensive patterns

Strategy: try-catch

Validate before calling

// probe bind capability cheaply before real start
try (ServerSocket probe = new ServerSocket()) {
    probe.bind(new InetSocketAddress("0.0.0.0", 0));
} catch (IOException e) {
    throw new GradleException("Host cannot bind 0.0.0.0:0 — APM mock will fail", e);
}

Try / catch

try {
    mockServer = new MockApmServer(...);
    mockServer.start();
} catch (IOException e) {
    throw new GradleException("Unable to start APM server: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: MockApmServer.start() throws IOException from HttpServer.create(new InetSocketAddress("0.0.0.0", 0), 10) — typically because the socket could not be bound (the OS chose port 0, so binding failures are rare unless the ephemeral range is exhausted or a security manager blocks it), or from ServerBuilder.forPort(0).start() for the gRPC server (e.g. native transport unavailable).

Common situations: Ephemeral port exhaustion on a heavily loaded CI host; a security manager / SELinux policy denying bind; gRPC native transport loading failure; running on a host where 0.0.0.0 bind is disallowed in a container with restricted net caps.

Related errors


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