apache/druid · error · IllegalStateException
No such stage [%s]
Error message
No such stage [%s]
What it means
QueryDefinitionBuilder.getStageBuilder(stageNumber) looks up a previously added stage builder by number and throws ISE when no builder with that number exists. It is used by helpers like leftBuilder/rightBuilder to wire inputs into existing stages, so a wrong or out-of-range stage number breaks plan assembly.
Source
Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/kernel/QueryDefinitionBuilder.java:82
}
/**
* Returns a number that is higher than all current stage numbers.
*/
public int getNextStageNumber()
{
return stageBuilders.stream().mapToInt(StageDefinitionBuilder::getStageNumber).max().orElse(-1) + 1;
}
public StageDefinitionBuilder getStageBuilder(final int stageNumber)
{
for (final StageDefinitionBuilder stageBuilder : stageBuilders) {
if (stageBuilder.getStageNumber() == stageNumber) {
return stageBuilder;
}
}
throw new ISE("No such stage [%s]", stageNumber);
}
public QueryDefinition build()
{
final List<StageDefinition> stageDefinitions =
stageBuilders.stream().map(builder -> builder.build(queryId)).collect(Collectors.toList());
return QueryDefinition.create(stageDefinitions, null);
}
}
View on GitHub (pinned to 9b90983fd2)
Solutions
- Add the input stage builders first, then call leftBuilder/rightBuilder with their actual stage numbers
- Use StageDefinitionBuilder's assigned stage number (getStageNumber()) instead of hardcoding indices
- Check the builder's stage list size before referencing a stage number
Example fix
// before: hardcoded stage number builder.add(StageDefinition.builder().id(new StageId(queryId, 2)).leftStageNumber(0)...); StageDefinitionBuilder left = builder.leftBuilder(0); // ok, but fragile with wrong guesses // after int leftNum = builder.add(leftInputDef).getStageNumber(); int rightNum = builder.add(rightInputDef).getStageNumber(); StageDefinitionBuilder left = builder.getStageBuilder(leftNum);
Defensive patterns
Strategy: validation
Validate before calling
if (builder.hasStage(stageNumber)) { // or track added numbers yourself
builder.getStageBuilder(stageNumber);
} else {
throw new IllegalArgumentException("Stage " + stageNumber + " not added to builder yet");
} Type guard
static boolean stageExists(List<StageDefinitionBuilder> builders, int stageNumber) {
return builders.stream().anyMatch(b -> b.getStageNumber() == stageNumber);
} Try / catch
try {
return builder.getStageBuilder(stageNumber);
} catch (IllegalStateException e) {
throw new PlanBuildException("Referenced stage not yet added: " + stageNumber, e);
} Prevention
- Add input stages before wiring left/right inputs
- Capture returned stage numbers instead of hardcoding them
- Remember stage numbers are zero-based sequential from the builder
When it happens
Trigger: Calling leftBuilder/rightBuilder (or getStageBuilder directly) with a stage number that was never added to this builder — e.g. building a join stage that references input stage numbers not yet added or numbered differently than assumed.
Common situations: Manual stage numbering mistakes in multi-stage plan construction; assuming stage numbers start at 1 when they start at 0 (or vice versa); referencing stages of another query's builder.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Stage [%s] is missing a definition
- Unknown
- Unknown
- No such outputChannelMode[%s]
- MSQFault from worker error report
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/c1e7a4a7d0b34b9c.
Report an issue: GitHub.