{"record":{"id":"48c1e3c3a9a92d91","repo":"apache/druid","slug":"maxchannelspermerger-d-2","errorCode":null,"errorMessage":"maxChannelsPerMerger[%d] < 2","messagePattern":"maxChannelsPerMerger\\[(.+?)\\] < 2","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/SuperSorter.java","lineNumber":273,"sourceCode":"    this.outputFrameType = outputFrameType;\n    this.maxChannelsPerMerger = maxChannelsPerMerger;\n    this.maxActiveProcessors = maxActiveProcessors;\n    this.rowLimit = rowLimit;\n    this.cancellationId = cancellationId;\n    this.superSorterProgressTracker = superSorterProgressTracker;\n    this.removeNullBytes = removeNullBytes;\n    this.combinerFactory = combinerFactory;\n\n    for (int i = 0; i < inputChannels.size(); i++) {\n      inputChannelsToRead.add(i);\n    }\n\n    if (maxActiveProcessors < 1) {\n      throw new IAE(\"maxActiveProcessors[%d] < 1\", maxActiveProcessors);\n    }\n\n    if (maxChannelsPerMerger < 2) {\n      throw new IAE(\"maxChannelsPerMerger[%d] < 2\", maxChannelsPerMerger);\n    }\n\n    if (rowLimit != UNLIMITED && rowLimit <= 0) {\n      throw new IAE(\"rowLimit[%d] must be positive\", rowLimit);\n    }\n  }\n\n  /**\n   * Starts sorting. Can only be called once. Work is performed in the {@link FrameProcessorExecutor} that was\n   * passed to the constructor.\n   *\n   * Returns a future containing partitioned sorted output channels.\n   */\n  public ListenableFuture<OutputChannels> run()\n  {\n    synchronized (runWorkersLock) {\n      if (allDone != null) {\n        throw new ISE(\"Cannot run() more than once.\");","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/SuperSorter.java#L255-L291","documentation":"SuperSorter's constructor validates that maxChannelsPerMerger, the maximum number of input channels merged in one merge step, is at least 2. A value below 2 would make merging impossible (a merger needs at least two inputs to combine), so the constructor throws IllegalArgumentException immediately. This is a fail-fast guard against a nonsensical configuration.","triggerScenarios":"Constructing a SuperSorter (directly or via ClippedQSegmentManager/processor config plumbing) with maxChannelsPerMerger set to 0, 1, or any negative number — typically from a misconfigured tuning parameter (e.g. druid processing config or MultiStageQuery tuning config) that was set too low.","commonSituations":"Operators lowering 'maxChannelsPerMerger' to reduce memory pressure and accidentally setting it to 1; copy-pasting tuning config from another cluster with a typo; programmatically computing the value from a partition/worker count that came out as 0 or 1 (e.g. dividing by zero-guarded counts); test harnesses passing hardcoded small values.","solutions":["Set maxChannelsPerMerger to at least 2 when constructing SuperSorter.","Check the originating configuration source (worker/task tuning config) and raise the value to 2 or more.","If the value is computed at runtime, add a lower bound of 2 where it is calculated (Math.max(2, computed)).","Check for typos or unit confusion in config files that feed this parameter."],"exampleFix":"// before\nSuperSorter sorter = new SuperSorter(maxActiveProcessors, 1, rowLimit, ...);\n// after\nSuperSorter sorter = new SuperSorter(maxActiveProcessors, Math.max(2, maxChannelsPerMerger), rowLimit, ...);","handlingStrategy":"validation","validationCode":"if (maxChannelsPerMerger < 2) {\n  throw new IllegalArgumentException(\"maxChannelsPerMerger must be >= 2, got \" + maxChannelsPerMerger);\n}","typeGuard":"static boolean isValidMaxChannelsPerMerger(int v) {\n  return v >= 2;\n}","tryCatchPattern":null,"preventionTips":["Clamp tuning params with Math.max(2, value) at config-load time.","Never hardcode 0/1 for merge fan-in.","Document the >= 2 constraint next to the config key.","Add a unit test asserting constructor rejects values < 2."],"tags":["config","validation","sorting"],"backgroundTag":"invalid-config-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}