apache/druid · error · IllegalArgumentException

rowLimit[ ] must be positive

Error message

rowLimit[%d] must be positive

What it means

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.

Solutions

  1. Pass SuperSorter.UNLIMITED instead of 0 when no row limit is desired.
  2. Ensure finite rowLimit values are >= 1 before constructing SuperSorter.
  3. If the limit comes from config or a query parameter, validate/clamp it to a positive integer at the call site.
  4. Use Math.max(1, limit) for computed finite limits, or translate 0 to UNLIMITED explicitly.

Example fix

// before
long rowLimit = 0; // intended: no limit
SuperSorter sorter = new SuperSorter(maxActiveProcessors, maxChannelsPerMerger, rowLimit, ...);
// after
long rowLimit = SuperSorter.UNLIMITED; // or Math.max(1, requestedLimit)
SuperSorter sorter = new SuperSorter(maxActiveProcessors, maxChannelsPerMerger, rowLimit, ...);
Defensive patterns

Strategy: validation

Validate before calling

if (rowLimit != SuperSorter.UNLIMITED && rowLimit <= 0) {
  throw new IllegalArgumentException("rowLimit must be positive or UNLIMITED, got " + rowLimit);
}

Type guard

static boolean isValidRowLimit(long rowLimit) {
  return rowLimit == SuperSorter.UNLIMITED || rowLimit > 0;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/91c5e998a4c2eceb. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/frame/processor/SuperSorter.java:277

    this.cancellationId = cancellationId;
    this.superSorterProgressTracker = superSorterProgressTracker;
    this.removeNullBytes = removeNullBytes;
    this.combinerFactory = combinerFactory;

    for (int i = 0; i < inputChannels.size(); i++) {
      inputChannelsToRead.add(i);
    }

    if (maxActiveProcessors < 1) {
      throw new IAE("maxActiveProcessors[%d] < 1", maxActiveProcessors);
    }

    if (maxChannelsPerMerger < 2) {
      throw new IAE("maxChannelsPerMerger[%d] < 2", maxChannelsPerMerger);
    }

    if (rowLimit != UNLIMITED && rowLimit <= 0) {
      throw new IAE("rowLimit[%d] must be positive", rowLimit);
    }
  }

  /**
   * Starts sorting. Can only be called once. Work is performed in the {@link FrameProcessorExecutor} that was
   * passed to the constructor.
   *
   * Returns a future containing partitioned sorted output channels.
   */
  public ListenableFuture<OutputChannels> run()
  {
    synchronized (runWorkersLock) {
      if (allDone != null) {
        throw new ISE("Cannot run() more than once.");
      }

      allDone = SettableFuture.create();
      runWorkersIfPossible();

View on GitHub (pinned to 9b90983fd2)