{"record":{"id":"f045d7106bfe7c21","repo":"apache/druid","slug":"result-partition-information-is-not-relevant-to-th","errorCode":null,"errorMessage":"Result partition information is not relevant to this stage because it does not shuffle","messagePattern":"Result partition information is not relevant to this stage because it does not shuffle","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/kernel/controller/ControllerStageTracker.java","lineNumber":253,"sourceCode":"  /**\n   * Partitions for the results of the stage associated with this tracker.\n   */\n  ReadablePartitions getResultPartitions()\n  {\n    if (resultPartitions == null) {\n      throw new ISE(\"Result partition information is not ready yet\");\n    } else {\n      return resultPartitions;\n    }\n  }\n\n  /**\n   * @return Partition boundaries for the results of this stage\n   */\n  ClusterByPartitions getResultPartitionBoundaries()\n  {\n    if (!getStageDefinition().doesShuffle()) {\n      throw new ISE(\"Result partition information is not relevant to this stage because it does not shuffle\");\n    } else if (resultPartitionBoundaries == null) {\n      throw new ISE(\"Result partition information is not ready yet\");\n    } else {\n      return resultPartitionBoundaries;\n    }\n  }\n\n\n  /**\n   * Get workers which need to be sent partition boundaries\n   *\n   * @return\n   */\n  IntSet getWorkersToSendPartitionBoundaries()\n  {\n    if (!getStageDefinition().doesShuffle()) {\n      throw new ISE(\"Result partition information is not relevant to this stage because it does not shuffle\");\n    }","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/kernel/controller/ControllerStageTracker.java#L235-L271","documentation":"getResultPartitionBoundaries is only meaningful for stages that shuffle (cluster-by) their output. If the stage definition does not shuffle, partition boundaries never exist, so the tracker throws ISE explaining the information is irrelevant rather than returning a meaningless null/boundaries set.","triggerScenarios":"Calling getResultPartitionBoundaries() on a ControllerStageTracker whose StageDefinition.doesShuffle() is false — e.g. final/result stages, non-shuffling leaf stages, or stages whose output channel mode is MEMORY without clustering.","commonSituations":"Controller or extension code iterating all stages of a query and calling getResultPartitionBoundaries unconditionally; debugging partition boundaries for an ingestion's final stage; misidentifying which stage performs the sort/cluster step.","solutions":["Guard the call with getStageDefinition().doesShuffle() and skip non-shuffling stages.","Use getResultPartitions() for non-shuffling stages when you only need the partition count.","If boundaries are needed for shuffle tuning, target the stage whose spec has a ClusterBy (the shuffle stage), typically not the final stage."],"exampleFix":"// before\nClusterByPartitions b = tracker.getResultPartitionBoundaries();\n\n// after\nif (tracker.getStageDefinition().doesShuffle()) {\n  ClusterByPartitions b = tracker.getResultPartitionBoundaries();\n}","handlingStrategy":"type-guard","validationCode":"if (!tracker.getStageDefinition().doesShuffle()) {\n  return null; // boundaries are meaningless for non-shuffling stages\n}","typeGuard":"ClusterByPartitions boundariesIfShuffling(ControllerStageTracker t) {\n  return t.getStageDefinition().doesShuffle() ? t.getResultPartitionBoundaries() : null;\n}","tryCatchPattern":"try {\n  ClusterByPartitions b = tracker.getResultPartitionBoundaries();\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"does not shuffle\")) {\n    b = null; // expected for non-shuffling stages\n  } else {\n    throw e;\n  }\n}","preventionTips":["Gate boundary reads on StageDefinition.doesShuffle().","Identify the shuffle stage (ClusterBy spec) instead of iterating all stages blindly.","Use getResultPartitions() when only the partition count is needed."],"tags":["msq","controller","shuffle","precondition"],"backgroundTag":"unsupported-operation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}