{"record":{"id":"706fff8ef2d90dc2","repo":"apache/druid","slug":"can-t-start-706fff","errorCode":null,"errorMessage":"can't start.","messagePattern":"can't start\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/curator/discovery/CuratorDruidNodeDiscoveryProvider.java","lineNumber":129,"sourceCode":"          log.debug(\"Creating NodeRoleWatcher for nodeRole [%s].\", role);\n          NodeRoleWatcher nodeRoleWatcher = new NodeRoleWatcher(\n              listenerExecutor,\n              curatorFramework,\n              config.getInternalDiscoveryPath(),\n              jsonMapper,\n              role\n          );\n          log.debug(\"Created NodeRoleWatcher for nodeRole [%s].\", role);\n          return nodeRoleWatcher;\n        }\n    );\n  }\n\n  @LifecycleStart\n  public void start()\n  {\n    if (!lifecycleLock.canStart()) {\n      throw new ISE(\"can't start.\");\n    }\n\n    try {\n      // This is single-threaded to ensure that all listener calls are executed precisely in the order of add/remove\n      // event occurrences.\n      listenerExecutor = Execs.scheduledSingleThreaded(\"CuratorDruidNodeDiscoveryProvider-ListenerExecutor\");\n\n      log.debug(\"Started.\");\n\n      lifecycleLock.started();\n    }\n    finally {\n      lifecycleLock.exitStart();\n    }\n  }\n\n  @LifecycleStop\n  public void stop() throws IOException","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/curator/discovery/CuratorDruidNodeDiscoveryProvider.java#L111-L147","documentation":"CuratorDruidNodeDiscoveryProvider.start() is annotated @LifecycleStart and uses a LifecycleLock permitting only one successful start. If canStart() returns false (already started, concurrently starting, or previously stopped), it throws ISE(\"can't start.\").","triggerScenarios":"Calling start() twice, calling start() concurrently from two threads, or calling start() after stop() without re-creating the instance; observed via the testAnnouncementAndDiscovery path.","commonSituations":"Double-registration of the provider in a Lifecycle, race between server startup and a test calling start directly, restart attempts on a stopped instance.","solutions":["Start the provider once per instance; create a new instance to restart","Use Druid's Lifecycle to manage ordering instead of manual start calls","Synchronize callers or use lifecycleLock.awaitStarted() to wait rather than double-start"],"exampleFix":"// before\nprovider.start();\nprovider.start();\n// after\nprovider.start(); // once; later callers should wait via lifecycleLock.awaitStarted()","handlingStrategy":"try-catch","validationCode":"if (!lifecycleLock.canStart()) {\n  throw new IllegalStateException(\"Provider already started or starting\");\n}","typeGuard":"boolean needsStart = lifecycleLock.canStart();","tryCatchPattern":"try {\n  provider.start();\n} catch (IllegalStateException e) {\n  if (\"can't start.\".equals(e.getMessage())) {\n    lifecycleLock.awaitStarted(); // already started: just wait for readiness\n  } else {\n    throw e;\n  }\n}","preventionTips":["Start the provider exactly once per instance","Use a shared Lifecycle instead of manual start calls","Avoid racing start() from multiple threads"],"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"}