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
- Set requestHandler (and other handlers) before calling server.listen().
- If the server is already bound, close() it, reconfigure, then listen() again.
- 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
- Register all handlers before calling listen()
- Enforce a single startup method that wires handlers then binds
- Never mutate a running server's handlers
- Use actualPort() to assert bind state in lifecycle code
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
- Already started
- Event Bus is not started
- Already closed
- Resource manager shutdown
- Resource manager closed
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/84b75f5563b6b7e7.
Report an issue: GitHub.