{"record":{"id":"e36862eacf94d845","repo":"apache/druid","slug":"can-t-stop-e36862","errorCode":null,"errorMessage":"can't stop.","messagePattern":"can't stop\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/db/updater/CoordinatorBasicAuthenticatorMetadataStorageUpdater.java","lineNumber":213,"sourceCode":"                LOG.makeAlert(t, \"Error occurred while polling for cachedUserMaps.\").emit();\n              }\n              return ScheduledExecutors.Signal.REPEAT;\n            }\n          }\n      );\n\n      lifecycleLock.started();\n    }\n    finally {\n      lifecycleLock.exitStart();\n    }\n  }\n\n  @LifecycleStop\n  public void stop()\n  {\n    if (!lifecycleLock.canStop()) {\n      throw new ISE(\"can't stop.\");\n    }\n\n    LOG.info(\"CoordinatorBasicAuthenticatorMetadataStorageUpdater is stopping.\");\n    stopped = true;\n    LOG.info(\"CoordinatorBasicAuthenticatorMetadataStorageUpdater is stopped.\");\n  }\n\n  @Override\n  public void createUser(String prefix, String userName)\n  {\n    Preconditions.checkState(lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));\n    createUserInternal(prefix, userName);\n  }\n\n  @Override\n  public void deleteUser(String prefix, String userName)\n  {\n    Preconditions.checkState(lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/db/updater/CoordinatorBasicAuthenticatorMetadataStorageUpdater.java#L195-L231","documentation":"Thrown by CoordinatorBasicAuthenticatorMetadataStorageUpdater.stop when lifecycleLock.canStop() returns false. stop() is only legal from the started state; calling it before start, twice, or concurrently triggers this ISE. The method would otherwise mark the updater stopped so subsequent user-map updates are rejected.","triggerScenarios":"stop() invoked without a prior successful start(); duplicate stop during shutdown; two threads racing to stop; lifecycle framework emitting stop events twice for one start.","commonSituations":"JVM shutdown hooks plus explicit lifecycle stop; test teardown stopping an already-stopped updater; leadership revocation handlers firing redundantly.","solutions":["Track started/stopped in the caller and skip redundant stop calls","Ensure the lifecycle lock usage matches Druid conventions (canStop/stopInState)","In tests, pair every start with exactly one stop in teardown","If the updater is already stopped, treat further stops as no-ops"],"exampleFix":"// before\nupdater.stop(); updater.stop(); // second call throws\n// after\nif (started) { updater.stop(); started = false; }","handlingStrategy":"try-catch","validationCode":"if (!updaterStarted.get()) { return; } // only stop after successful start\nupdater.stop(); updaterStarted.set(false);","typeGuard":null,"tryCatchPattern":"try { updater.stop(); } catch (ISE e) { LOG.info(\"stop ignored: updater not started\"); }","preventionTips":["Make stop idempotent with a started flag at the call site","Don't register multiple stop hooks for the same updater","In tests, teardown should stop at most once after a start","Check lifecycle logs for prior stop events before stopping"],"tags":["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"}