eclipse-vertx/vert.x · error · IllegalStateException

Server already bound

Error message

Server already bound

What it means

QuicHttpServer.requestHandler rejects handler changes once the server is bound (actualPort > 0). Setting a request handler after the server is listening is unsupported because handlers must be in place before connections arrive; Vert.x throws IllegalStateException to prevent silent misconfiguration.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/quic/QuicHttpServer.java:72

  public QuicHttpServer(VertxInternal vertx, HttpServerConfig config, ServerSSLOptions sslOptions, HttpServerMetrics<?, ?> httpMetrics) {

    // We own the copy
    sslOptions.setApplicationLayerProtocols(Arrays.asList(Http3.supportedApplicationProtocols()));

    this.vertx = vertx;
    this.config = config;
    this.sslOptions = sslOptions;
    this.http3Config = config.getHttp3Config() != null ? config.getHttp3Config() : new Http3ServerConfig();
    this.quicConfig = config.getQuicConfig() != null ? config.getQuicConfig() : new QuicServerConfig();
    this.actualPort = 0;
    this.httpMetrics = httpMetrics;
    this.manageMetrics = httpMetrics == null;
  }

  @Override
  public HttpServer requestHandler(Handler<HttpServerRequest> handler) {
    if (actualPort > 0) {
      throw new IllegalStateException("Server already bound");
    }
    this.requestHandler = handler;
    return this;
  }

  @Override
  public Handler<HttpServerRequest> requestHandler() {
    return requestHandler;
  }

  @Override
  public HttpServer invalidRequestHandler(Handler<HttpServerRequest> handler) {
    if (actualPort > 0) {
      throw new IllegalStateException("Server already bound");
    }
    return this;
  }

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Set requestHandler (and other handlers) before calling server.listen().
  2. If the server is already bound, close() it, reconfigure, then listen() again.
  3. Restructure startup so configuration is fully built before binding, e.g. build handlers first then a single listen call.

Example fix

// before
server.listen(8443).onSuccess(s -> s.requestHandler(handler)); // too late
// after
server.requestHandler(handler);
server.listen(8443);
Defensive patterns

Strategy: validation

Validate before calling

if (server.actualPort() > 0) throw new IllegalStateException("set handlers before listen()");

Try / catch

try { server.requestHandler(h); } catch (IllegalStateException e) { server.close().onSuccess(v -> { server.requestHandler(h); server.listen(port); }); }

Prevention

When it happens

Trigger: Calling server.requestHandler(h) after listen() completed and reported an actual port > 0; reconfiguring a bound server from a lifecycle callback.

Common situations: Deferred wiring of handlers until after startup; hot-reload logic that mutates a running server; order bugs where listen() is awaited before handler setup.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/84b75f5563b6b7e7. Report an issue: GitHub.