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
- Read the wrapped IOException's cause for the bind/grpc failure detail (the top-level message may be empty).
- If ephemeral ports are exhausted, reduce concurrent testclusters / raise net.ipv4.ip_local_port_range.
- Disable gRPC native transport if the failure is native-lib loading: set -Dio.grpc.netty.useNativeTransport=false in the gradle JVM.
- 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
- If APM is not needed, set apmServerEnabled=false on the RunTask.
- On loaded CI hosts, raise the ephemeral port range or reduce concurrency.
- Inspect the wrapped IOException cause — the top-level message may be null.
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
- MockApmServer already started
- MockApmServer not started
- Number of nodes should be >= 1 but was {} for {}
- Cannot shrink {} to have {} nodes as it already has {}
- Cannot add nodes to test cluster after is has been frozen
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/fd08274ae9ccf2b8.
Report an issue: GitHub.