{"record":{"id":"6d95e10e216e5a6e","repo":"apache/druid","slug":"partition-count-must-be-1-when-adjustable-is-true-6d95e1","errorCode":null,"errorMessage":"Partition count must be 1 when adjustable is true, but was [%d]","messagePattern":"Partition count must be 1 when adjustable is true, but was \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/kernel/HashShuffleSpec.java","lineNumber":50,"sourceCode":"  public static final String TYPE = \"hash\";\n\n  private final ClusterBy clusterBy;\n  private final int numPartitions;\n  private final boolean adjustable;\n\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  {","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/kernel/HashShuffleSpec.java#L32-L68","documentation":"HashShuffleSpec with adjustable=true means the controller may re-shard at runtime starting from a single partition, so the constructor requires numPartitions == 1. Passing adjustable=true with any other partition count violates that contract and throws IAE. Adjustable hash shuffles must seed with exactly one partition.","triggerScenarios":"Constructing HashShuffleSpec(clusterBy, numPartitions, adjustable=true) where numPartitions != 1 — e.g. ShuffleSpecFactory.create for HASH kind with adjustable workers config and a partition count > 1.","commonSituations":"Query context settings that both set an explicit multi-partition count and enable adjustable partitioning (e.g. dynamic worker scaling with fixed partitions); miswired factory parameters.","solutions":["Set numPartitions to 1 when adjustable is true, letting the controller grow partitions dynamically","Set adjustable to false if a fixed numPartitions > 1 is genuinely required","Audit the call site (ShuffleSpecFactory.create) so the partition count passed for adjustable shuffles is always 1"],"exampleFix":"// before\nnew HashShuffleSpec(clusterBy, 4, true);\n// after\nnew HashShuffleSpec(clusterBy, 1, true); // adjustable: start at 1\n// or, for fixed 4 partitions\nnew HashShuffleSpec(clusterBy, 4, false);","handlingStrategy":"validation","validationCode":"if (adjustable && numPartitions != 1) {\n  throw new IllegalArgumentException(\"Adjustable hash shuffles must start with numPartitions = 1\");\n}","typeGuard":"static boolean validHashSpecParams(int numPartitions, boolean adjustable) {\n  return !adjustable || numPartitions == 1;\n}","tryCatchPattern":"try {\n  spec = new HashShuffleSpec(clusterBy, numPartitions, adjustable);\n} catch (IllegalArgumentException e) {\n  spec = new HashShuffleSpec(clusterBy, 1, adjustable); // or set adjustable=false\n}","preventionTips":["Start adjustable shuffles at 1 partition and let the controller scale","Set adjustable=false when a fixed partition count > 1 is required","Centralize shuffle construction in ShuffleSpecFactory to keep these invariants in one place"],"tags":["msq","shuffle-spec","invalid-argument","hash-partitioning"],"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"}