{"record":{"id":"b71eae16789f0e26","repo":"apache/druid","slug":"could-not-start-lifecycle","errorCode":null,"errorMessage":"Could not start lifecycle","messagePattern":"Could not start lifecycle","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/client/HttpServerInventoryView.java","lineNumber":157,"sourceCode":"  {\n    this.httpClient = httpClient;\n    this.smileMapper = smileMapper;\n    this.druidNodeDiscoveryProvider = druidNodeDiscoveryProvider;\n    this.defaultFilter = defaultFilter;\n    this.finalPredicate = defaultFilter;\n    this.config = config;\n    this.serviceEmitter = serviceEmitter;\n    this.executorFactory = executorFactory;\n    this.execNamePrefix = execNamePrefix;\n  }\n\n\n  @LifecycleStart\n  public void start()\n  {\n    synchronized (lifecycleLock) {\n      if (!lifecycleLock.canStart()) {\n        throw new ISE(\"Could not start lifecycle\");\n      }\n\n      log.info(\"Starting executor[%s].\", execNamePrefix);\n\n      try {\n        inventorySyncExecutor = executorFactory.create(\n            config.getNumThreads(),\n            execNamePrefix + \"-%s\"\n        );\n        monitoringExecutor = executorFactory.create(1, execNamePrefix + \"-monitor-%s\");\n\n        DruidNodeDiscovery druidNodeDiscovery = druidNodeDiscoveryProvider.getForService(DataNodeService.DISCOVERY_SERVICE_KEY);\n        druidNodeDiscovery.registerListener(\n            new DruidNodeDiscovery.Listener()\n            {\n              private final AtomicBoolean initialized = new AtomicBoolean(false);\n\n              @Override","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/client/HttpServerInventoryView.java#L139-L175","documentation":"HttpServerInventoryView.start() guards startup with a LifecycleLock. If canStart() returns false — the lifecycle is already started, currently starting, or has been stopped — it throws this ISE. It signals an illegal lifecycle state transition rather than a startup failure of the executor itself.","triggerScenarios":"Calling start() twice without an intervening stop(); calling start() after stop() (lifecycle lock in stopped state); concurrent start() invocations racing on lifecycleLock.","commonSituations":"Registering the same HttpServerInventoryView instance with two Lifecycle objects (e.g. both server and test harness start it); wiring it into a guice lifecycle twice; restart logic that calls start() without reset of lifecycle state.","solutions":["Ensure start() is called exactly once per instance; use a new instance instead of restarting the old one","Check for double registration in guice module bindings or Lifecycle.addStartHook duplicates","In tests, construct a fresh HttpServerInventoryView per test instead of reusing a static instance","If a restart is required, stop() the instance first and verify lifecycleLock state before starting"],"exampleFix":"// before\nview.start();\n...\nview.start(); // ISE: Could not start lifecycle\n// after\nif (!started) {\n  view.start();\n  started = true;\n}","handlingStrategy":"try-catch","validationCode":"// guard\nprivate final AtomicBoolean started = new AtomicBoolean(false);\nif (!started.compareAndSet(false, true)) return;\nview.start();","typeGuard":null,"tryCatchPattern":"try { view.start(); } catch (IllegalStateException e) { log.warn(\"lifecycle already %s\", e.getMessage()); }","preventionTips":["Start each lifecycle-managed instance exactly once","Avoid sharing HttpServerInventoryView across Lifecycles","Construct fresh instances in tests"],"tags":["lifecycle","illegal-state","druid"],"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"}