{"record":{"id":"91c5e998a4c2eceb","repo":"apache/druid","slug":"rowlimit-d-must-be-positive","errorCode":null,"errorMessage":"rowLimit[%d] must be positive","messagePattern":"rowLimit\\[(.+?)\\] must be positive","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/SuperSorter.java","lineNumber":277,"sourceCode":"    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.\");\n      }\n\n      allDone = SettableFuture.create();\n      runWorkersIfPossible();","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/SuperSorter.java#L259-L295","documentation":"SuperSorter's constructor validates the optional rowLimit: it may be UNLIMITED (no limit) or any strictly positive number, but not zero or negative. A non-positive finite rowLimit cannot be meaningfully interpreted, so IllegalArgumentException is thrown at construction time. UNLIMITED is explicitly permitted and skips the positivity check.","triggerScenarios":"Constructing a SuperSorter with a finite rowLimit value of 0 or less — e.g. passing a limit read from user input or query config where the caller used 0 to mean 'no limit' instead of SuperSorter.UNLIMITED, or a computed limit that underflowed to 0/negative.","commonSituations":"Callers confusing 0 with 'unlimited' (common convention elsewhere); query parameters like a row-limit or top-N setting arriving as 0 from a UI or API; integer arithmetic on limits (e.g. remaining = limit - consumed) reaching 0 or below before being passed in; test code passing default int 0.","solutions":["Pass SuperSorter.UNLIMITED instead of 0 when no row limit is desired.","Ensure finite rowLimit values are >= 1 before constructing SuperSorter.","If the limit comes from config or a query parameter, validate/clamp it to a positive integer at the call site.","Use Math.max(1, limit) for computed finite limits, or translate 0 to UNLIMITED explicitly."],"exampleFix":"// before\nlong rowLimit = 0; // intended: no limit\nSuperSorter sorter = new SuperSorter(maxActiveProcessors, maxChannelsPerMerger, rowLimit, ...);\n// after\nlong rowLimit = SuperSorter.UNLIMITED; // or Math.max(1, requestedLimit)\nSuperSorter sorter = new SuperSorter(maxActiveProcessors, maxChannelsPerMerger, rowLimit, ...);","handlingStrategy":"validation","validationCode":"if (rowLimit != SuperSorter.UNLIMITED && rowLimit <= 0) {\n  throw new IllegalArgumentException(\"rowLimit must be positive or UNLIMITED, got \" + rowLimit);\n}","typeGuard":"static boolean isValidRowLimit(long rowLimit) {\n  return rowLimit == SuperSorter.UNLIMITED || rowLimit > 0;\n}","tryCatchPattern":null,"preventionTips":["Use SuperSorter.UNLIMITED, not 0, for 'no limit'.","Sanitize user/query-supplied limits: translate 0 to UNLIMITED or reject.","Validate limits at the API/config boundary, before object construction.","Watch for subtraction-based limit arithmetic that can reach 0."],"tags":["config","validation","sorting"],"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-17T15:17:12.973Z"}