SonarSource/sonarqube · error · IllegalStateException

Already started

Error message

Already started

What it means

ProcessEntryPoint.launch guards against double-startup by moving the lifecycle to STARTING atomically. If the process (or this entry point) is already started, the transition fails and IllegalStateException 'Already started' is thrown.

Solutions

  1. Create a new ProcessEntryPoint instance for each launch
  2. Ensure launch is called only once per JVM/process
  3. Check the lifecycle state before calling launch
  4. Refactor tests to instantiate a fresh entry point per test

Example fix

// before
ProcessEntryPoint entry = ProcessEntryPoint.create(props);
entry.launch(monitored);
entry.launch(monitored); // throws
// after
ProcessEntryPoint entry = ProcessEntryPoint.create(props);
entry.launch(monitored); // single launch per instance
Defensive patterns

Strategy: validation

Validate before calling

if (lifecycle.getState() != Lifecycle.State.INIT) {
  throw new IllegalStateException("ProcessEntryPoint already launched");
}

Try / catch

try {
  entryPoint.launch(monitored);
} catch (IllegalStateException e) {
  if ("Already started".equals(e.getMessage())) { LOGGER.warn("launch called twice; ignoring"); } else throw e;
}

Prevention

When it happens

Trigger: Calling launch(monitor) twice on the same ProcessEntryPoint instance, or reusing a shutdown/restarted lifecycle.

Common situations: Testing code calling launch repeatedly on a shared entry point; wrapper scripts relaunching in the same JVM; accidental double invocation in main.

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 SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/4d2159abade15e48. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java:75

    this.stopWatcher = createStopWatcher(commands, this);
    this.hardStopWatcher = createHardStopWatcher(commands, this);
    this.runtime = runtime;
  }

  public ProcessCommands getCommands() {
    return commands;
  }

  public Props getProps() {
    return props;
  }

  /**
   * Launch process and waits until it's down
   */
  public void launch(Monitored mp) {
    if (!lifecycle.tryToMoveTo(Lifecycle.State.STARTING)) {
      throw new IllegalStateException("Already started");
    }
    monitored = mp;

    Logger logger = LoggerFactory.getLogger(getClass());
    try {
      launch(logger);
    } catch (Exception e) {
      logger.warn("Fail to start {}", processId.getHumanReadableName(), e);
      hardStop();
    }
  }

  private void launch(Logger logger) throws InterruptedException {
    logger.info("Starting {}", processId.getHumanReadableName());
    runtime.addShutdownHook(new Thread(() -> {
      exit.setInShutdownHook();
      stop();
    }));

View on GitHub (pinned to 184c821202)