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
- Call start() only once per emitter instance; guard with isStarted()/lifecycle state before calling
- If restart is needed, create a new HttpPostEmitter instance instead of reusing the old one
- 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
- Start each HttpPostEmitter exactly once, managed via Lifecycle
- Guard restart logic: create a new instance rather than re-calling start
- Avoid calling start from multiple init paths (module + manual setup)
- Use isStarted()/lifecycle state checks before invoking start
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.