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.

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)

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.