SonarSource/sonarqube · warning

Pool did not terminate

Error message

Pool {} did not terminate

What it means

AbstractStoppableExecutorService.stop performs an orderly shutdown, waits 5s, calls shutdownNow, and waits another 5s; if the pool still has not terminated it logs this warning. It means some tasks ignored interruption and are still running while the component is being stopped — possible resource leaks or delayed shutdown.

Solutions

  1. Make worker tasks responsive to interruption: check Thread.currentThread().isInterrupted() and honor InterruptedException
  2. Add timeouts to blocking calls (HTTP client timeouts, socket timeouts, DB query timeouts)
  3. Check for non-daemon long-running tasks submitted to this pool and bound their work
  4. If it is a stuck third-party call, isolate it in a separate executor or use futures with get(timeout)

Example fix

// before
while (true) { process(next()); }
// after
while (!Thread.currentThread().isInterrupted()) {
  process(next());
}
Defensive patterns

Strategy: fallback

Try / catch

try {
  pool.shutdown();
  if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
    pool.shutdownNow();
    if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
      log.warn("Pool {} did not terminate; tasks may be stuck", name);
    }
  }
} catch (InterruptedException ie) {
  pool.shutdownNow();
  Thread.currentThread().interrupt();
}

Prevention

When it happens

Trigger: stop() is called during server/component shutdown while a submitted Runnable/Callable ignores interrupts (long loop without interrupt checks) or blocks on non-interruptible I/O, so both awaitTermination(5s) calls return false.

Common situations: Blocking network/DB calls without timeout in worker tasks; infinite processing loops in tasks; many queued tasks that cannot finish within the grace period; shutdown during heavy load.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/a893fc2d3f9ace7d. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-server-common/src/main/java/org/sonar/server/util/AbstractStoppableExecutorService.java:54

public abstract class AbstractStoppableExecutorService<D extends ExecutorService> implements StoppableExecutorService {
  protected final D delegate;

  public AbstractStoppableExecutorService(D delegate) {
    this.delegate = delegate;
  }

  @Override
  public void stop() {
    // Disable new tasks from being submitted
    delegate.shutdown();
    try {
      // Wait a while for existing tasks to terminate
      if (!delegate.awaitTermination(5, TimeUnit.SECONDS)) {
        // Cancel currently executing tasks
        delegate.shutdownNow();
        // Wait a while for tasks to respond to being canceled
        if (!delegate.awaitTermination(5, TimeUnit.SECONDS)) {
          LoggerFactory.getLogger(getClass()).warn("Pool {} did not terminate", getClass().getSimpleName());
        }
      }
    } catch (InterruptedException ie) {
      LoggerFactory.getLogger(getClass()).warn("Termination of pool {} failed", getClass().getSimpleName(), ie);
      // (Re-)Cancel if current thread also interrupted
      delegate.shutdownNow();
    }
  }

  @Override
  public void shutdown() {
    delegate.shutdown();
  }

  @Override
  public List<Runnable> shutdownNow() {
    return delegate.shutdownNow();
  }

View on GitHub (pinned to 184c821202)