{"record":{"id":"f31458864dce56f6","repo":"apache/druid","slug":"lookupcoordinatormanager-can-t-stop","errorCode":null,"errorMessage":"LookupCoordinatorManager can't stop.","messagePattern":"LookupCoordinatorManager can't stop\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManager.java","lineNumber":457,"sourceCode":"\n        LOG.debug(\"Started\");\n      }\n      catch (Exception ex) {\n        LOG.makeAlert(ex, \"Got Exception while start()\").emit();\n      }\n      finally {\n        //so that subsequent stop() would happen, even if start() failed with exception\n        lifecycleLock.started();\n        lifecycleLock.exitStart();\n      }\n    }\n  }\n\n  public void stop()\n  {\n    synchronized (lifecycleLock) {\n      if (!lifecycleLock.canStop()) {\n        throw new ISE(\"LookupCoordinatorManager can't stop.\");\n      }\n\n      try {\n        LOG.debug(\"Stopping\");\n\n        if (backgroundManagerFuture != null && !backgroundManagerFuture.cancel(true)) {\n          LOG.warn(\"Background lookup manager thread could not be cancelled\");\n        }\n\n        // signal the executorService to shut down ASAP, if this coordinator becomes leader again\n        // then start() would ensure that this executorService is finished before starting a\n        // new one.\n        if (executorService != null) {\n          executorService.shutdownNow();\n        }\n\n        LOG.debug(\"Stopped\");\n      }","sourceCodeStart":439,"sourceCodeEnd":475,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManager.java#L439-L475","documentation":"LookupCoordinatorManager.stop() throws this IllegalStateException when the lifecycle lock reports the component cannot be stopped — i.e. stop() was called before start() ever succeeded, or after the component was already stopped. Druid uses LifecycleLock to enforce strict start/stop ordering on managed components; stopping an un-started or already-stopped coordinator is treated as a programming error rather than an idempotent no-op.","triggerScenarios":"Calling LookupCoordinatorManager.stop() when start() was never called, when a start() attempt failed midway leaving the lock in a non-started state, or calling stop() twice on the same instance. The listed tests exercise multiple start/stop cycles which can hit this if ordering is wrong.","commonSituations":"Wiring the coordinator into a Druid lifecycle in the wrong order, a failed start (e.g. injected HttpServerStartupError) followed by a stop attempt, unit tests that call stop() without start(), or double-shutdown of a server that stops the same manager twice.","solutions":["Ensure start() is called (and succeeds) before stop() on the same LookupCoordinatorManager instance","Guard stop() calls with the lifecycle state, e.g. only stop if the coordinator was started","Avoid double-stopping: track shutdown state at the caller level or use Druid's Lifecycle management so stop is invoked exactly once","If start() failed, fix the underlying start error (injection/config) instead of calling stop() afterwards"],"exampleFix":"// before\nmanager.stop(); // called without ever starting\n// after\nif (!started) {\n  manager.start();\n}\nmanager.stop();","handlingStrategy":"try-catch","validationCode":"if (manager != null && lifecycleLock.isStarted()) { /* safe to stop */ }","typeGuard":"boolean isStoppable(LifecycleLock lock) { return lock.isStarted() && !lock.isStopped(); }","tryCatchPattern":"try {\n  manager.stop();\n} catch (IllegalStateException e) {\n  if (!e.getMessage().contains(\"can't stop\")) throw e;\n  LOG.debug(\"Coordinator already stopped or never started\");\n}","preventionTips":["Always pair start()/stop() on the same instance and track the started state","Register the manager with a Druid Lifecycle so shutdown ordering is handled automatically","In tests, call start() in @BeforeEach and stop() in @AfterEach exactly once","Never call stop() in a failure path of a failed start()"],"tags":["lifecycle","illegal-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-17T15:17:12.973Z"}