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
- Use a unique listenerId per registration, or reuse the same one intentionally and skip re-registration
- Unregister (removeListener) before registering again with the same ID
- Check the listeners list (or track locally) before calling registerListener
- 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
- Use a unique listenerId per subscriber
- Call removeListener before re-registering
- Avoid sharing listenerId strings across services/tests
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());
}
@OverrideView on GitHub (pinned to 9b90983fd2)