{"record":{"id":"3b2b1bdc71f6fe2d","repo":"apache/druid","slug":"can-t-stop-3b2b1b","errorCode":null,"errorMessage":"can't stop.","messagePattern":"can't stop\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"extensions-core/kubernetes-extensions/src/main/java/org/apache/druid/k8s/discovery/K8sDruidLeaderSelector.java","lineNumber":153,"sourceCode":"    }\n    try {\n      this.listener = listener;\n      startLeaderElector(leaderLatch);\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    closeLeaderLatchQuietly();\n  }\n\n  private void closeLeaderLatchQuietly()\n  {\n    CloseableUtils.closeAndSuppressExceptions(\n        leaderLatch,\n        e -> LOGGER.warn(\"Exception caught while cleaning up leader latch\")\n    );\n  }\n}\n","sourceCodeStart":135,"sourceCodeEnd":167,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/kubernetes-extensions/src/main/java/org/apache/druid/k8s/discovery/K8sDruidLeaderSelector.java#L135-L167","documentation":"unregisterListener() requires LifecycleLock.canStop(); if the selector was never started, is already stopped, or is mid-startup, an IllegalStateException \"can't stop.\" is thrown. It enforces balanced start/stop of the leader elector.","triggerScenarios":"Calling unregisterListener on a K8sDruidLeaderSelector that never had registerListener succeed, or calling it twice / concurrently with a start in progress.","commonSituations":"Shutdown hooks firing after an earlier stop; cleanup paths running when listener registration failed earlier; double-stop in tests.","solutions":["Track whether the selector is started; only call unregisterListener after a successful registerListener.","Make cleanup idempotent by catching/ignoring ISE in shutdown paths.","Wait for any in-flight registerListener to finish before stopping.","If a start failed, recreate the selector rather than stopping it."],"exampleFix":"// before\n@Override public void shutdown() { leaderSelector.unregisterListener(); } // throws if never started\n// after\n@Override public void shutdown() { try { leaderSelector.unregisterListener(); } catch (IllegalStateException e) { LOG.debug(\"not started\"); } }","handlingStrategy":"try-catch","validationCode":"// only stop if you started\nboolean started = false;\n// at register: started = true;  at unregister: if (!started) return;","typeGuard":null,"tryCatchPattern":"try {\n  leaderSelector.unregisterListener();\n} catch (IllegalStateException e) {\n  LOG.debug(e, \"Selector was not started; nothing to stop\");\n}","preventionTips":["Keep start/stop balanced in the same component; guard with a boolean or lifecycle manager.","Make shutdown hooks idempotent.","Do not unregister after a failed registerListener; discard the instance instead.","Serialize lifecycle calls across threads."],"tags":["leader-election","lifecycle","illegal-state","shutdown"],"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-14T05:17:10.506Z"}