{"record":{"id":"3c38d06cc72b6102","repo":"apache/druid","slug":"can-t-stop-3c38d0","errorCode":null,"errorMessage":"can't stop.","messagePattern":"can't stop\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/curator/discovery/CuratorDruidLeaderSelector.java","lineNumber":205,"sourceCode":"\n      createNewLeaderLatchWithListener();\n      leaderLatch.get().start();\n\n      lifecycleLock.started();\n    }\n    catch (Exception ex) {\n      throw new RuntimeException(ex);\n    }\n    finally {\n      lifecycleLock.exitStart();\n    }\n  }\n\n  @Override\n  public void unregisterListener()\n  {\n    if (!lifecycleLock.canStop()) {\n      throw new ISE(\"can't stop.\");\n    }\n\n    CloseableUtils.closeAndSuppressExceptions(leaderLatch.get(), e -> log.warn(e, \"Failed to close LeaderLatch.\"));\n    listenerExecutor.shutdownNow();\n  }\n\n  private void stopAndCreateNewLeaderLatch()\n  {\n    CloseableUtils.closeAndSuppressExceptions(\n        createNewLeaderLatchWithListener(),\n        e -> log.warn(\"Could not close old leader latch; continuing with new one anyway.\")\n    );\n  }\n\n  private void startLeaderLatch()\n  {\n    try {\n      //Small delay before starting the latch so that others waiting are chosen to become leader.","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/curator/discovery/CuratorDruidLeaderSelector.java#L187-L223","documentation":"CuratorDruidLeaderSelector.unregisterListener is guarded by the LifecycleLock; if canStop() returns false (never started, already stopped, or concurrently stopping), it throws ISE(\"can't stop.\"). It enforces start-before-stop lifecycle ordering.","triggerScenarios":"Calling unregisterListener() before start() completed, calling it twice, or after start() failed so the lock never reached the started state.","commonSituations":"Shutdown hooks firing on partially-initialized services, tests stopping components without starting them, exception during start leaving the lock in a bad state.","solutions":["Ensure start() succeeds before calling unregisterListener()","Call unregisterListener()/stop only once per lifecycle","Guard callers with lifecycleLock.awaitStarted() or check state before stopping"],"exampleFix":"// before\nleaderSelector.unregisterListener();\n// after\nif (lifecycleLock.awaitStarted(1, TimeUnit.SECONDS)) {\n  leaderSelector.unregisterListener();\n}","handlingStrategy":"try-catch","validationCode":"if (!lifecycleLock.canStop()) {\n  log.warn(\"LeaderSelector not started; skipping unregister\");\n  return;\n}","typeGuard":"boolean safeToUnregister = lifecycleLock.canStop();","tryCatchPattern":"try {\n  leaderSelector.unregisterListener();\n} catch (IllegalStateException e) {\n  if (\"can't stop.\".equals(e.getMessage())) {\n    log.debug(\"selector never started; ignoring unregister\");\n  } else {\n    throw e;\n  }\n}","preventionTips":["Pair start()/unregisterListener() in try/finally","Let Druid Lifecycle manage start/stop ordering","Never unregister after a failed start; rebuild the instance"],"tags":["lifecycle","zookeeper","curator","state"],"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-14T11:17:12.474Z"}