apache/druid · error · IllegalStateException

Already started.

Error message

Already started.

What it means

HttpPostEmitter.start() is lifecycle-guarded: after a successful start (startLatch count reaches 0), calling start() again throws IllegalStateException 'Already started.' The emitter's background EmittingThread must only be started once per emitter instance.

Solutions

  1. Call start() only once per emitter instance; guard with isStarted()/lifecycle state before calling
  2. If restart is needed, create a new HttpPostEmitter instance instead of reusing the old one
  3. Fix duplicate lifecycle registration so start isn't invoked from two places

Example fix

// before
emitter.start();
emitter.start(); // throws Already started.
// after
if (!emitter.isStarted()) {
  emitter.start();
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (emitter.isStarted()) {
  throw new IllegalStateException("Emitter already started; create a new instance to restart");
}

Type guard

boolean canStart(HttpPostEmitter e) {
  return !e.isStarted();
}

Try / catch

try {
  emitter.start();
} catch (IllegalStateException e) {
  log.warn("Emitter already started; ignoring duplicate start");
}

Prevention

When it happens

Trigger: Calling start() a second time on the same HttpPostEmitter instance after it has already been started (startLatch.getCount() == 0 and still running).

Common situations: Lifecycle double-initialization in frameworks wiring both a module and a manual start; test setup calling start in both a fixture and the test body; restart logic that recreates config but reuses the old emitter instance.

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 apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/cb78403495782447. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/java/util/emitter/core/HttpPostEmitter.java:192

    }
    catch (MalformedURLException e) {
      throw new ISE(e, "Bad URL: %s", config.getRecipientBaseUrl());
    }
    emittingThread = new EmittingThread(config);
    long firstBatchNumber = 1;
    concurrentBatch.set(new Batch(this, acquireBuffer(), firstBatchNumber));
    // lastBatchFillTimeMillis must not be 0, minHttpTimeoutMillis could be.
    lastBatchFillTimeMillis = Math.max(config.minHttpTimeoutMillis, 1);
  }

  @Override
  @LifecycleStart
  public void start()
  {
    synchronized (startLock) {
      if (!running) {
        if (startLatch.getCount() == 0) {
          throw new IllegalStateException("Already started.");
        }
        running = true;
        startLatch.countDown();
        emittingThread.start();
      }
    }
  }

  private void awaitStarted()
  {
    try {
      if (!startLatch.await(1, TimeUnit.SECONDS)) {
        throw new RejectedExecutionException("Service is not started.");
      }
      if (isTerminated()) {
        throw new RejectedExecutionException("Service is closed.");
      }
    }

View on GitHub (pinned to 9b90983fd2)