elastic/elasticsearch · error · IllegalStateException
MockApmServer already started
Error message
MockApmServer already started
What it means
Thrown by MockApmServer.start() if the instance field is already non-null — i.e. start() was called twice on the same MockApmServer object. The server is single-use: start() binds an HttpServer on 0.0.0.0:0 and a gRPC ServerBuilder on port 0, storing references in instance/grpcInstance.
Source
Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/MockApmServer.java:102
private Pattern createWildcardPattern(String filter) {
if (filter == null || filter.isEmpty()) {
return null;
}
var pattern = Arrays.stream(filter.split(",\\s*"))
.map(Pattern::quote)
.map(s -> s.replace("*", "\\E.*\\Q"))
.collect(Collectors.joining(")|(", "(", ")"));
return Pattern.compile(pattern);
}
/**
* Start the Mock APM server. Just returns empty JSON structures for every incoming message
*
* @throws IOException
*/
public void start() throws IOException {
if (instance != null) {
throw new IllegalStateException("MockApmServer already started");
}
InetSocketAddress addr = new InetSocketAddress("0.0.0.0", 0);
HttpServer server = HttpServer.create(addr, 10);
server.createContext("/", new RootHandler());
server.start();
instance = server;
logger.lifecycle("MockApmServer started on port " + server.getAddress().getPort());
grpcInstance = ServerBuilder.forPort(0).addService(new GrpcMetricsService()).addService(new GrpcTraceService()).build().start();
logger.lifecycle("MockApmServer gRPC (OTLP metrics + traces) started on port " + grpcInstance.getPort());
}
public int getPort() {
if (instance == null) {
throw new IllegalStateException("MockApmServer not started");
}
return instance.getAddress().getPort();
}View on GitHub (pinned to db6a809a66)
Solutions
- Call start() exactly once per MockApmServer instance — guard with 'if (mockServer == null) { mockServer = new MockApmServer(...); mockServer.start(); }'.
- If you need a fresh server, call stop() first and assign a new instance.
- Let RunTask own the lifecycle (apmServerEnabled flag) rather than calling start() yourself.
Example fix
// before:
mockServer.start(); // called twice → throws
// after:
if (mockServer == null) {
mockServer = new MockApmServer(metrics, txns, excludes);
mockServer.start();
} Defensive patterns
Strategy: validation
Validate before calling
if (instance != null) {
throw new IllegalStateException("MockApmServer already started");
} Prevention
- Call start() exactly once per MockApmServer instance.
- Guard with a null check before starting.
- Let RunTask own the APM lifecycle via apmServerEnabled.
When it happens
Trigger: Code calls mockServer.start() a second time without stop()/nulling the instance. In RunTask this can happen if a custom task wires start() into a lifecycle hook that fires more than once, or if the same MockApmServer instance is shared across tasks.
Common situations: Custom RunTask subclass that calls start() in both a doFirst and the default action; reusing a static MockApmServer across gradle tasks in the same build; a test fixture that starts APM in @BeforeAll without checking prior state.
Related errors
- MockApmServer not started
- Configuration for {} can not be altered, already locked
- Cannot add nodes to test cluster after is has been frozen
- Configuration for {} can not be altered, already locked
- Can't treat {} as single node as it has {} nodes
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/95891eb0cacd3e20.
Report an issue: GitHub.