TooTallNate/Java-WebSocket · error · IllegalStateException

Cannot call setDaemon after server is already started!

Error message

Cannot call setDaemon after server is already started!

What it means

setDaemon() propagates the daemon flag to the decoder WebSocketWorker threads, which are created in the constructor. If any decoder thread is already alive the server is running and thread daemon status can no longer be changed, so an IllegalStateException is thrown.

Solutions

  1. Call setDaemon() before start(), right after construction.
  2. Check whether the server has started (e.g. track a started flag or selectorthread) and skip/reject the setDaemon call otherwise.
  3. To change daemon mode at runtime, stop the server, create a new instance, setDaemon, then start.

Example fix

// before
server.start();
server.setDaemon(true); // throws: decoders alive
// after
server.setDaemon(true);
server.start();
Defensive patterns

Strategy: validation

Validate before calling

if (server.selectorthread != null) {
  throw new IllegalStateException("setDaemon must be called before start()");
}
server.setDaemon(daemon);

Try / catch

try {
  server.setDaemon(daemon);
} catch (IllegalStateException e) {
  log.warn("Cannot change daemon mode after start; applying on next restart");
}

Prevention

When it happens

Trigger: Calling server.setDaemon(true/false) after start() (or run()) has spawned the decoder threads; flipping daemon mode dynamically at runtime.

Common situations: Config applied asynchronously after server startup; a settings UI updating daemon mode while the server is live; initialization-order bugs where configuration arrives post-start.

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 TooTallNate/Java-WebSocket@afeacbf8c0 (2026-09-09). Data as JSON: /api/errors/c9f163d29af7b9cc. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/java_websocket/server/WebSocketServer.java:362

   *
   * @return The port number.
   */
  public int getPort() {
    int port = getAddress().getPort();
    if (port == 0 && server != null) {
      port = server.socket().getLocalPort();
    }
    return port;
  }

  @Override
  public void setDaemon(boolean daemon) {
    // pass it to the AbstractWebSocket too, to use it on the connectionLostChecker thread factory
    super.setDaemon(daemon);
    // we need to apply this to the decoders as well since they were created during the constructor
    for (WebSocketWorker w : decoders) {
      if (w.isAlive()) {
        throw new IllegalStateException("Cannot call setDaemon after server is already started!");
      } else {
        w.setDaemon(daemon);
      }
    }
  }

  /**
   * Get the list of active drafts
   *
   * @return the available drafts for this server
   */
  public List<Draft> getDraft() {
    return Collections.unmodifiableList(drafts);
  }

  /**
   * Set the requested maximum number of pending connections on the socket. The exact semantics are
   * implementation specific. The value provided should be greater than 0. If it is less than or

View on GitHub (pinned to afeacbf8c0)