apache/druid · error · RejectedExecutionException

Service is closed.

Error message

Service is closed.

What it means

HttpPostEmitter queues events into batches and pushes them to a remote HTTP endpoint. awaitStarted() is called on every emit/flush path; it throws RejectedExecutionException('Service is closed.') when the emitter's emitting thread has been terminated (isTerminated() true), i.e. events are being submitted after close(). The library rejects work once the service lifecycle has ended rather than silently dropping events.

Source

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

      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.");
      }
    }
    catch (InterruptedException e) {
      log.debug("Interrupted waiting for start");
      Thread.currentThread().interrupt();
      throw new RuntimeException(e);
    }
  }

  private boolean isTerminated()
  {
    return concurrentBatch.get() == null;
  }

  @Override
  public void emit(Event event)
  {
    emitAndReturnBatch(event);

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Ensure the emitter is started before use and not closed while events may still arrive: move emitter close() to the very end of shutdown sequencing.
  2. Guard emit calls with an isTerminated()/started check or wrap producers so they stop before the emitter closes.
  3. Catch RejectedExecutionException around emit/flush and drop or buffer events during shutdown.
  4. If this occurs at startup instead, verify Emitter.start() was called and completed within 1 second (startLatch await).

Example fix

// before
emitter.close();
reportMetrics(emitter); // throws RejectedExecutionException
// after
reportMetrics(emitter);
emitter.close();
Defensive patterns

Strategy: try-catch

Validate before calling

if (emitter instanceof HttpPostEmitter && ((HttpPostEmitter) emitter).isTerminated()) {
  log.warn("Emitter closed; skipping emit");
  return;
}

Try / catch

try {
  emitter.emit(event);
} catch (RejectedExecutionException e) {
  log.debug(e, "Emitter closed, event dropped: %s", event);
}

Prevention

When it happens

Trigger: Calling emit(), flush(), or close()-time flush after HttpPostEmitter.close()/stop() has terminated the EmittingThread; a race where a producer thread emits while another thread closes the emitter; Lifecycle stop ordering where metrics emitters are shut down before components still emitting.

Common situations: Shutdown ordering bugs in Druid services (emitter stopped while real-time metrics still flowing); application code caching an Emitter reference after lifecycle.stop(); frameworks flushing telemetry during JVM shutdown after the emitter was closed.

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/82b5bb945e03d9bb. Report an issue: GitHub.