{"record":{"id":"23f20463d3d8d480","repo":"apache/druid","slug":"cannot-bucket-with-s-partitioning-clusterby-s","errorCode":null,"errorMessage":"Cannot bucket with %s partitioning (clusterBy = %s)","messagePattern":"Cannot bucket with (.+?) partitioning \\(clusterBy = (.+?)\\)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/kernel/HashShuffleSpec.java","lineNumber":55,"sourceCode":"\n  @JsonCreator\n  public HashShuffleSpec(\n      @JsonProperty(\"clusterBy\") final ClusterBy clusterBy,\n      @JsonProperty(\"partitions\") final int numPartitions,\n      @JsonProperty(\"adjustable\") final boolean adjustable\n  )\n  {\n    this.clusterBy = clusterBy;\n    this.numPartitions = numPartitions;\n    this.adjustable = adjustable;\n\n    if (adjustable && numPartitions != 1) {\n      throw new IAE(\"Partition count must be 1 when adjustable is true, but was [%d]\", numPartitions);\n    }\n\n    if (clusterBy.getBucketByCount() > 0) {\n      // Only GlobalSortTargetSizeShuffleSpec supports bucket-by.\n      throw new IAE(\"Cannot bucket with %s partitioning (clusterBy = %s)\", TYPE, clusterBy);\n    }\n  }\n\n  @Override\n  public ShuffleKind kind()\n  {\n    return clusterBy.sortable() && !clusterBy.isEmpty() ? ShuffleKind.HASH_LOCAL_SORT : ShuffleKind.HASH;\n  }\n\n  @Override\n  @JsonProperty\n  public ClusterBy clusterBy()\n  {\n    return clusterBy;\n  }\n\n  @Override\n  @JsonProperty(\"partitions\")","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/kernel/HashShuffleSpec.java#L37-L73","documentation":"Bucket-by keys (clusterBy.getBucketByCount() > 0) are only supported by GlobalSortTargetSizeShuffleSpec. When a HashShuffleSpec is constructed with a ClusterBy containing bucket-by keys, the constructor rejects it with IAE, including the clusterBy for diagnosis.","triggerScenarios":"Building a HashShuffleSpec (HASH shuffle kind via ShuffleSpecFactory.create) from a ClusterBy with bucket-by columns, e.g. an MSQ query using time-bucketed partitioning with hash shuffling.","commonSituations":"Queries that specify bucket (time) partitioning while the engine selects hash partitioning; configuration mixing CLUSTERED BY time columns with hash-based distribution.","solutions":["Remove the bucket-by keys from the ClusterBy before creating the hash shuffle spec","Use GlobalSortTargetSizeShuffleSpec (TARGET_SIZE kind) if bucket-by is required","Choose a sort-based partitioning strategy rather than hash when bucket keys are present"],"exampleFix":"// before\nnew HashShuffleSpec(clusterByWithBucketBy, numPartitions, false);\n// after\nnew HashShuffleSpec(clusterBy.withoutBucketBy(), numPartitions, false);","handlingStrategy":"validation","validationCode":"if (clusterBy.getBucketByCount() > 0) {\n  throw new IllegalArgumentException(\"Bucket-by requires GlobalSortTargetSizeShuffleSpec, not hash\");\n}\nnew HashShuffleSpec(clusterBy, numPartitions, adjustable);","typeGuard":"static boolean hashSupportsClusterBy(ClusterBy clusterBy) {\n  return clusterBy == null || clusterBy.getBucketByCount() == 0;\n}","tryCatchPattern":"try {\n  spec = new HashShuffleSpec(clusterBy, numPartitions, adjustable);\n} catch (IllegalArgumentException e) {\n  spec = new GlobalSortTargetSizeShuffleSpec(clusterBy, targetSize, false);\n}","preventionTips":["Strip bucket-by keys before hashing (clusterBy.withoutBucketBy() or equivalent)","Use TARGET_SIZE shuffles for bucketed queries","Validate bucketBy counts in query planning before shuffle-spec creation"],"tags":["msq","shuffle-spec","bucket-by","invalid-argument"],"backgroundTag":"invalid-argument-value","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"}