{"record":{"id":"cb78403495782447","repo":"apache/druid","slug":"already-started","errorCode":null,"errorMessage":"Already started.","messagePattern":"Already started\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/java/util/emitter/core/HttpPostEmitter.java","lineNumber":192,"sourceCode":"    }\n    catch (MalformedURLException e) {\n      throw new ISE(e, \"Bad URL: %s\", config.getRecipientBaseUrl());\n    }\n    emittingThread = new EmittingThread(config);\n    long firstBatchNumber = 1;\n    concurrentBatch.set(new Batch(this, acquireBuffer(), firstBatchNumber));\n    // lastBatchFillTimeMillis must not be 0, minHttpTimeoutMillis could be.\n    lastBatchFillTimeMillis = Math.max(config.minHttpTimeoutMillis, 1);\n  }\n\n  @Override\n  @LifecycleStart\n  public void start()\n  {\n    synchronized (startLock) {\n      if (!running) {\n        if (startLatch.getCount() == 0) {\n          throw new IllegalStateException(\"Already started.\");\n        }\n        running = true;\n        startLatch.countDown();\n        emittingThread.start();\n      }\n    }\n  }\n\n  private void awaitStarted()\n  {\n    try {\n      if (!startLatch.await(1, TimeUnit.SECONDS)) {\n        throw new RejectedExecutionException(\"Service is not started.\");\n      }\n      if (isTerminated()) {\n        throw new RejectedExecutionException(\"Service is closed.\");\n      }\n    }","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/java/util/emitter/core/HttpPostEmitter.java#L174-L210","documentation":"HttpPostEmitter.start() is lifecycle-guarded: after a successful start (startLatch count reaches 0), calling start() again throws IllegalStateException 'Already started.' The emitter's background EmittingThread must only be started once per emitter instance.","triggerScenarios":"Calling start() a second time on the same HttpPostEmitter instance after it has already been started (startLatch.getCount() == 0 and still running).","commonSituations":"Lifecycle double-initialization in frameworks wiring both a module and a manual start; test setup calling start in both a fixture and the test body; restart logic that recreates config but reuses the old emitter instance.","solutions":["Call start() only once per emitter instance; guard with isStarted()/lifecycle state before calling","If restart is needed, create a new HttpPostEmitter instance instead of reusing the old one","Fix duplicate lifecycle registration so start isn't invoked from two places"],"exampleFix":"// before\nemitter.start();\nemitter.start(); // throws Already started.\n// after\nif (!emitter.isStarted()) {\n  emitter.start();\n}","handlingStrategy":"type-guard","validationCode":"if (emitter.isStarted()) {\n  throw new IllegalStateException(\"Emitter already started; create a new instance to restart\");\n}","typeGuard":"boolean canStart(HttpPostEmitter e) {\n  return !e.isStarted();\n}","tryCatchPattern":"try {\n  emitter.start();\n} catch (IllegalStateException e) {\n  log.warn(\"Emitter already started; ignoring duplicate start\");\n}","preventionTips":["Start each HttpPostEmitter exactly once, managed via Lifecycle","Guard restart logic: create a new instance rather than re-calling start","Avoid calling start from multiple init paths (module + manual setup)","Use isStarted()/lifecycle state checks before invoking start"],"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"}