{"record":{"id":"164f25030bf201f0","repo":"apache/druid","slug":"cannot-start-the-stage-s","errorCode":null,"errorMessage":"Cannot start the stage: [%s]","messagePattern":"Cannot start the stage: \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/kernel/controller/ControllerQueryKernel.java","lineNumber":595,"sourceCode":"  public Object getResultObjectForStage(final StageId stageId)\n  {\n    return getStageTrackerOrThrow(stageId).getResultObject();\n  }\n\n  /**\n   * Checks if the stage can be started, delegates call to {@link ControllerStageTracker#start()} for internal phase\n   * transition and registers the transition in this queryKernel. Work orders need to be created via\n   * {@link ControllerQueryKernel#createWorkOrders(int, int, Int2ObjectMap)} before calling this method.\n   */\n  public void startStage(final StageId stageId)\n  {\n    if (stageWorkOrders.get(stageId) == null) {\n      throw new ISE(\"Work order not present for stage[%s]\", stageId);\n    }\n\n    doWithStageTracker(stageId, stageTracker -> {\n      if (stageTracker.getPhase() != ControllerStagePhase.NEW) {\n        throw new ISE(\"Cannot start the stage: [%s]\", stageId);\n      }\n\n      stageTracker.start();\n    });\n  }\n\n  /**\n   * Checks if the stage can be finished, delegates call to {@link ControllerStageTracker#finish()} for internal phase\n   * transition and registers the transition in this query kernel\n   * <p>\n   * If the method is called with strict = true, we confirm if the stage can be marked as finished or else\n   * throw illegal argument exception\n   */\n  public void finishStage(final StageId stageId, final boolean strict)\n  {\n    if (strict && !effectivelyFinishedStages.contains(stageId)) {\n      throw new IAE(\"Cannot mark the stage: [%s] finished\", stageId);\n    }","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/kernel/controller/ControllerQueryKernel.java#L577-L613","documentation":"Thrown by ControllerQueryKernel.startStage when the stage's ControllerStageTracker is not in ControllerStagePhase.NEW, meaning the stage has already been started (or finished/failed). Starting a stage is a one-time phase transition, so a repeat call is an illegal state.","triggerScenarios":"Calling startStage(stageId) twice for the same stage, or calling it on a stage already in READING_INPUT / POST_READING / RESULTS_COMPLETE / FINISHED phases — typically via startWorkForStage after a state-machine re-entry or duplicate stage-start event.","commonSituations":"Duplicate success/counter messages from workers causing the controller to re-run the stage-start step; idempotency bugs in controller retry logic; state-machine replay after controller restart re-executing an already-applied transition.","solutions":["Check the stage phase before calling startStage (only proceed when phase == ControllerStagePhase.NEW)","Deduplicate the controller event/callback that triggers startWorkForStage so the transition is applied once","If this occurs after restart, confirm replay logic marks already-started stages instead of re-starting them","Make the call idempotent in caller code by catching ISE and treating an already-started stage as a no-op"],"exampleFix":"// before\nkernel.startStage(stageId);\n\n// after\nif (kernel.getStagePhase(stageId) == ControllerStagePhase.NEW) {\n  kernel.startStage(stageId);\n}","handlingStrategy":"type-guard","validationCode":"if (queryKernel.getStagePhase(stageId) == ControllerStagePhase.NEW) {\n  queryKernel.startStage(stageId);\n}","typeGuard":"boolean isNewPhase(ControllerQueryKernel kernel, StageId stageId) {\n  try {\n    return kernel.getStagePhase(stageId) == ControllerStagePhase.NEW;\n  } catch (IllegalArgumentException e) {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  queryKernel.startStage(stageId);\n} catch (IllegalStateException e) {\n  LOG.debug(e, \"Stage %s already started; treating as no-op\", stageId);\n}","preventionTips":["Make stage-start events idempotent in the state machine","Guard with a phase check before each transition","Deduplicate worker callbacks that re-trigger startWorkForStage","On replay after restart, skip transitions already recorded in the kernel"],"tags":["msq","state-machine","controller","duplicate-operation"],"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"}