{"record":{"id":"c8bc38210651f31f","repo":"apache/druid","slug":"handler-already-registered-for-service-s","errorCode":null,"errorMessage":"handler already registered for service[%s]","messagePattern":"handler already registered for service\\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/segment/realtime/ChatHandlerProvider.java","lineNumber":58,"sourceCode":"\n  @Inject\n  public ChatHandlerProvider()\n  {\n    this.handlers = new ConcurrentHashMap<>();\n  }\n\n  /**\n   * Registers a chat handler which provides an API for others to talk to objects in the indexing service.\n   *\n   * @param service a unique name identifying this service\n   * @param handler instance which implements the API to be exposed\n   */\n  public void register(final String service, ChatHandler handler)\n  {\n    log.debug(\"Registering Eventhandler[%s]\", service);\n\n    if (handlers.putIfAbsent(service, handler) != null) {\n      throw new ISE(\"handler already registered for service[%s]\", service);\n    }\n  }\n\n  /**\n   * Unregisters a chat handler.\n   *\n   * @param service the name of the service\n   */\n  public void unregister(final String service)\n  {\n    log.debug(\"Unregistering chat handler[%s]\", service);\n\n    final ChatHandler handler = handlers.get(service);\n    if (handler == null) {\n      log.warn(\"handler[%s] not currently registered, ignoring.\", service);\n      return;\n    }\n","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/segment/realtime/ChatHandlerProvider.java#L40-L76","documentation":"ChatHandlerProviderImpl keeps a map from service name to ChatHandler. register() throws this ISE when a handler is already registered under the same service name, because putIfAbsent returns the existing entry — Druid does not allow silent replacement of chat handlers.","triggerScenarios":"Calling chatHandlerProvider.register(service, handler) twice with the same service string — e.g. re-initializing a task/runners within the same JVM, or two handlers registered with an identical service name.","commonSituations":"Kafka/Kinesis indexing tasks restarted in the same JVM lifecycle; a custom extension registering handlers with a name that collides with a built-in one; double registration due to lifecycle hooks firing twice.","solutions":["Unregister the existing handler first via chatHandlerProvider.unregister(service, handler) before re-registering","Ensure the registration code path runs only once per JVM lifecycle","Use a unique service name per handler instance if multiple handlers are needed","If intentionally replacing, refactor to explicitly close the old handler and unregister before creating the new one"],"exampleFix":"// before\nchatHandlerProvider.register(service, handler); // throws if already present\n// after\nchatHandlerProvider.unregister(service, handler);\nchatHandlerProvider.register(service, handler);","handlingStrategy":"validation","validationCode":"if (chatHandlerProvider.get(service) != null) {\n  chatHandlerProvider.unregister(service, existingHandler);\n}\nchatHandlerProvider.register(service, handler);","typeGuard":null,"tryCatchPattern":"try {\n  chatHandlerProvider.register(service, handler);\n} catch (ISE e) {\n  log.warn(e, \"Handler already registered for %s\", service);\n}","preventionTips":["Register each ChatHandler exactly once per JVM lifecycle","Use unique service names per handler instance","Call unregister in your lifecycle stop/shutdown hook"],"tags":["chat-handler","lifecycle","duplicate-registration"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}