apache/druid · error · IllegalStateException
Cannot start the stage: [%s]
Error message
Cannot start the stage: [%s]
What it means
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.
Source
Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/kernel/controller/ControllerQueryKernel.java:595
public Object getResultObjectForStage(final StageId stageId)
{
return getStageTrackerOrThrow(stageId).getResultObject();
}
/**
* Checks if the stage can be started, delegates call to {@link ControllerStageTracker#start()} for internal phase
* transition and registers the transition in this queryKernel. Work orders need to be created via
* {@link ControllerQueryKernel#createWorkOrders(int, int, Int2ObjectMap)} before calling this method.
*/
public void startStage(final StageId stageId)
{
if (stageWorkOrders.get(stageId) == null) {
throw new ISE("Work order not present for stage[%s]", stageId);
}
doWithStageTracker(stageId, stageTracker -> {
if (stageTracker.getPhase() != ControllerStagePhase.NEW) {
throw new ISE("Cannot start the stage: [%s]", stageId);
}
stageTracker.start();
});
}
/**
* Checks if the stage can be finished, delegates call to {@link ControllerStageTracker#finish()} for internal phase
* transition and registers the transition in this query kernel
* <p>
* If the method is called with strict = true, we confirm if the stage can be marked as finished or else
* throw illegal argument exception
*/
public void finishStage(final StageId stageId, final boolean strict)
{
if (strict && !effectivelyFinishedStages.contains(stageId)) {
throw new IAE("Cannot mark the stage: [%s] finished", stageId);
}View on GitHub (pinned to 9b90983fd2)
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
Example fix
// before
kernel.startStage(stageId);
// after
if (kernel.getStagePhase(stageId) == ControllerStagePhase.NEW) {
kernel.startStage(stageId);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (queryKernel.getStagePhase(stageId) == ControllerStagePhase.NEW) {
queryKernel.startStage(stageId);
} Type guard
boolean isNewPhase(ControllerQueryKernel kernel, StageId stageId) {
try {
return kernel.getStagePhase(stageId) == ControllerStagePhase.NEW;
} catch (IllegalArgumentException e) {
return false;
}
} Try / catch
try {
queryKernel.startStage(stageId);
} catch (IllegalStateException e) {
LOG.debug(e, "Stage %s already started; treating as no-op", stageId);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Work order not present for stage[%s]
- Cannot mark the stage: [%s] finished
- Stage[%d] work orders not found
- Work order for worker[%d] not found for stage[%d]
- Result partition information is not ready yet
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/164f25030bf201f0.
Report an issue: GitHub.