{"record":{"id":"4a970c37e735b2c2","repo":"apache/druid","slug":"must-have-a-single-final-stage-but-found-d-can","errorCode":null,"errorMessage":"Must have a single final stage, but found [%d] candidates","messagePattern":"Must have a single final stage, but found \\[(.+?)\\] candidates","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/kernel/QueryDefinition.java","lineNumber":110,"sourceCode":"    }\n\n    for (final StageId nonFinalStageId : nonFinalStages) {\n      if (!stageMap.containsKey(nonFinalStageId)) {\n        throw new ISE(\"Stage [%s] is missing a definition\", nonFinalStageId);\n      }\n    }\n\n    final int finalStageCandidates = stageMap.size() - nonFinalStages.size();\n\n    if (finalStageCandidates == 1) {\n\n      return new QueryDefinition(\n          stageMap,\n          Iterables.getOnlyElement(Sets.difference(stageMap.keySet(), nonFinalStages)),\n          context == null ? QueryContext.empty() : context\n      );\n    } else {\n      throw new IAE(\"Must have a single final stage, but found [%d] candidates\", finalStageCandidates);\n    }\n  }\n\n  public static QueryDefinitionBuilder builder(final String queryId)\n  {\n    return new QueryDefinitionBuilder(queryId);\n  }\n\n  public static QueryDefinitionBuilder builder(final QueryDefinition queryDef)\n  {\n    return new QueryDefinitionBuilder(queryDef.getQueryId()).addAll(queryDef);\n  }\n\n  public String getQueryId()\n  {\n    return finalStage.getQueryId();\n  }\n","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/kernel/QueryDefinition.java#L92-L128","documentation":"A QueryDefinition must have exactly one final stage (a stage no other stage consumes). create computes finalStageCandidates = total stages - non-final stages and throws IAE if the count is not exactly 1, because the query would be ambiguous (multiple roots or none).","triggerScenarios":"QueryDefinition.create where the stage graph has zero final stages (cycles / all stages consumed) or two or more final stages (disconnected subqueries or an extra unused root stage).","commonSituations":"Assembling multiple independent queries into one QueryDefinition; plan-rewriting bugs leaving orphan root stages; cycles introduced during plan merges making every stage non-final.","solutions":["Ensure the plan has exactly one root: merge extra roots or split them into separate QueryDefinitions","Remove cycles in input-stage references so at least one stage is never consumed","Fix builder/rewrite logic so only one stage remains unconsumed before calling create"],"exampleFix":"// before: two roots\nbuilder.add(scanDef); builder.add(scanDef2);\n// after: union the roots or keep only one per QueryDefinition\nQueryDefinition q1 = ...singleRootPlan...; QueryDefinition q2 = ...singleRootPlan...;","handlingStrategy":"validation","validationCode":"Set<StageId> nonFinal = new HashSet<>();\nfor (StageDefinition s : stageDefinitions) {\n  for (int n : s.getInputStageNumbers()) nonFinal.add(new StageId(s.getId().getQueryId(), n));\n}\nlong finals = stageDefinitions.size() - nonFinal.size();\nif (finals != 1) {\n  throw new IllegalArgumentException(\"Plan must have exactly 1 final stage, found \" + finals);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return QueryDefinition.create(stageDefinitions, context);\n} catch (IllegalArgumentException e) {\n  throw new PlanValidationException(\"Plan must have a single root/final stage\", e);\n}","preventionTips":["Ensure the stage graph is acyclic so at least one stage is never consumed","Split independent subqueries into separate QueryDefinitions","After plan rewrites, assert exactly one unconsumed stage remains"],"tags":["msq","query-definition","plan-structure","invalid-argument"],"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"}