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

  1. Call start() exactly once per MockApmServer instance — guard with 'if (mockServer == null) { mockServer = new MockApmServer(...); mockServer.start(); }'.
  2. If you need a fresh server, call stop() first and assign a new instance.
  3. 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

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


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