TooTallNate/Java-WebSocket · error · IllegalStateException
can only be started once.
Error message
can only be started once.
What it means
WebSocketServer.start() throws IllegalStateException if selectorthread is already set, i.e. the server instance was already started (or run() already executed). A server instance is single-use: once its selector thread exists it cannot be started again; create a new instance to restart.
Solutions
- Guard with a started flag or selectorthread check before calling start(), or catch IllegalStateException and treat it as already-running.
- To restart, stop() the server, create a NEW WebSocketServer instance, then call start() on it.
- In tests, build the server fresh in @BeforeEach instead of a shared @BeforeAll field.
Example fix
// before
server.start(); // second call after earlier start -> IllegalStateException
server.start();
// after
if (server.selectorthread == null) { // or track your own started flag
server.start();
}
// or for restarts:
server.stop();
server = new MyServer(address);
server.start(); Defensive patterns
Strategy: try-catch
Validate before calling
if (server.selectorthread != null) {
// already started — skip or restart with a new instance
} Type guard
static boolean notYetStarted(WebSocketServer s) { return s.selectorthread == null; } Try / catch
try {
server.start();
} catch (IllegalStateException e) {
log.info("Server already started, ignoring duplicate start");
} Prevention
- Track started state in your own wrapper and make start() idempotent
- For restarts always stop() then construct a fresh WebSocketServer instance
- In tests, create the server in @BeforeEach rather than sharing one instance across tests
When it happens
Trigger: Calling start() twice on the same WebSocketServer instance; calling start() after run() was invoked directly; test harnesses that start the server in setup and then start it again per test; restart logic that reuses the old object instead of constructing a fresh one.
Common situations: Application restart logic (config reload, watchdog) re-calling start(); Spring/CDI re-initialization invoking a @PostConstruct twice; JUnit tests sharing a server field across tests.
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
- Invalid SSL status:
- this method must be used in conjunction with flushAndClose
- Cannot call setDaemon after server is already started!
- buffer size < 0
- parameter must not be null
AI-assisted analysis of TooTallNate/Java-WebSocket@afeacbf8c0 (2026-09-09).
Data as JSON: /api/errors/0ca2376291865262.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/java_websocket/server/WebSocketServer.java:270
for (int i = 0; i < decodercount; i++) {
WebSocketWorker ex = new WebSocketWorker();
decoders.add(ex);
}
}
/**
* Starts the server selectorthread that binds to the currently set port number and listeners for
* WebSocket connection requests. Creates a fixed thread pool with the size {@link
* WebSocketServer#AVAILABLE_PROCESSORS}<br> May only be called once.
* <p>
* Alternatively you can call {@link WebSocketServer#run()} directly.
*
* @throws IllegalStateException Starting an instance again
*/
public void start() {
if (selectorthread != null) {
throw new IllegalStateException(getClass().getName() + " can only be started once.");
}
Thread t = new Thread(this);
t.setDaemon(isDaemon());
t.start();
}
public void stop(int timeout) throws InterruptedException {
stop(timeout, "");
}
/**
* Closes all connected clients sockets, then closes the underlying ServerSocketChannel,
* effectively killing the server socket selectorthread, freeing the port the server was bound to
* and stops all internal workerthreads.
* <p>
* If this method is called before the server is started it will never start.
*
* @param timeout Specifies how many milliseconds the overall close handshaking may takeView on GitHub (pinned to afeacbf8c0)