apache/druid · error · IllegalStateException

Listener [ ] already registered

Error message

Listener [%s] already registered

What it means

KubernetesTaskRunner.registerListener() throws this IllegalStateException when a listener with the same listenerId is already registered. The runner keeps a list of (listener, executor) pairs and enforces unique listener IDs so status-change notifications are delivered exactly once per subscriber.

Solutions

  1. Use a unique listenerId per registration, or reuse the same one intentionally and skip re-registration
  2. Unregister (removeListener) before registering again with the same ID
  3. Check the listeners list (or track locally) before calling registerListener
  4. In tests, use a fresh runner or distinct IDs per test case

Example fix

// before
runner.registerListener(listener, executor);
// re-subscribe path
runner.registerListener(listener, executor); // ISE
// after
runner.removeListener(listener);
runner.registerListener(listener, executor);
Defensive patterns

Strategy: try-catch

Validate before calling

boolean alreadyRegistered = registeredIds.contains(listener.getListenerId());
if (!alreadyRegistered) { taskRunner.registerListener(listener, executor); }

Try / catch

try {
  taskRunner.registerListener(listener, executor);
} catch (IllegalStateException e) {
  // listener already registered; skip or re-register after removeListener
}

Prevention

When it happens

Trigger: Registering the same TaskRunnerListener instance (or two listeners with equal getListenerId()) twice; re-registering after a restart of the calling component without unregistering first; multiple Overlord subsystems sharing one listenerId string.

Common situations: TaskMaster/re-subscription logic re-adding a listener on reconnect; tests registering a listener in setup for every case on a shared runner; two services both passing 'overlord' as listenerId.

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/21cdb978b8de6ccc. Report an issue: GitHub.

Appendix: source

Thrown at extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunner.java:535

  @Override
  public void unregisterListener(String listenerId)
  {
    for (Pair<TaskRunnerListener, Executor> pair : listeners) {
      if (pair.lhs != null && pair.lhs.getListenerId().equals(listenerId)) {
        listeners.remove(pair);
        log.debug("Unregistered listener [%s]", listenerId);
        return;
      }
    }
  }

  @Override
  public void registerListener(TaskRunnerListener listener, Executor executor)
  {
    for (Pair<TaskRunnerListener, Executor> pair : listeners) {
      if (pair.lhs != null && pair.lhs.getListenerId().equals(listener.getListenerId())) {
        throw new ISE("Listener [%s] already registered", listener.getListenerId());
      }
    }

    final Pair<TaskRunnerListener, Executor> listenerPair = Pair.of(listener, executor);
    log.debug("Registered listener [%s]", listener.getListenerId());
    listeners.add(listenerPair);
  }

  @Override
  public Collection<TaskRunnerWorkItem> getRunningTasks()
  {
    return tasks.values()
                .stream()
                .filter(KubernetesWorkItem::isRunning)
                .collect(Collectors.toList());
  }

  @Override

View on GitHub (pinned to 9b90983fd2)