{"record":{"id":"7446004e8dd65386","repo":"apache/druid","slug":"stage-s-is-missing-a-definition","errorCode":null,"errorMessage":"Stage [%s] is missing a definition","messagePattern":"Stage \\[(.+?)\\] is missing a definition","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/kernel/QueryDefinition.java","lineNumber":96,"sourceCode":"    final Map<StageId, StageDefinition> stageMap = new HashMap<>();\n    final Set<StageId> nonFinalStages = new HashSet<>();\n    final IntSet stageNumbers = new IntOpenHashSet();\n\n    for (final StageDefinition stage : stageDefinitions) {\n      if (!stageNumbers.add(stage.getStageNumber())) {\n        throw new ISE(\"Cannot accept duplicate stage numbers\");\n      }\n\n      stageMap.put(stage.getId(), stage);\n\n      for (int stageNumber : stage.getInputStageNumbers()) {\n        nonFinalStages.add(new StageId(stage.getId().getQueryId(), stageNumber));\n      }\n    }\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)","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/kernel/QueryDefinition.java#L78-L114","documentation":"After collecting all stages referenced as inputs (non-final stages), QueryDefinition.create verifies that every referenced input stage actually has a definition in the stage map. A dangling reference — a stage listed as an input of another stage but never defined — throws ISE, since the query would deadlock waiting for a stage that never runs.","triggerScenarios":"QueryDefinition.create with stages whose getInputStageNumbers() reference a stage number not present in stageDefinitions (e.g. a plan built with addInput referencing a stage never added, or a stage removed during plan rewriting without updating inputs).","commonSituations":"Plan-rewriting/optimization bugs that drop or renumber stages; hand-assembled multi-stage plans in tests or embedded engines; merging partial plans without their dependency stages.","solutions":["Ensure every input stage number has a corresponding StageDefinition added to the builder before building","Fix plan-rewrite logic to update/remove dangling input references when stages are dropped or renumbered","Validate the plan (all input numbers < stage count and defined) before calling create"],"exampleFix":"// before: input referenced but never added\nbuilder.add(StageDefinition...withInputStage(1)); // stage 1 never added\n// after\nbuilder.add(stage1Def); // add the dependency first\nbuilder.add(StageDefinition...withInputStage(1));","handlingStrategy":"validation","validationCode":"Set<Integer> defined = stageDefinitions.stream().map(StageDefinition::getStageNumber).collect(toSet());\nfor (StageDefinition stage : stageDefinitions) {\n  for (int input : stage.getInputStageNumbers()) {\n    if (!defined.contains(input)) {\n      throw new IllegalArgumentException(\"Stage \" + stage.getStageNumber() + \" references undefined input \" + input);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  return QueryDefinition.create(stageDefinitions, context);\n} catch (IllegalStateException e) {\n  throw new PlanValidationException(\"Dangling input stage reference\", e);\n}","preventionTips":["Add dependency stages to the builder before stages that consume them","After plan rewrites, sweep for input numbers not present in the stage map","Keep stage references symbolic (builder handles) rather than raw numbers"],"tags":["msq","query-definition","missing-stage","dangling-reference"],"backgroundTag":"entity-not-found","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"}