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
- 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.
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
- 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.
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
- maxChannelsPerMerger
- At least one of baseDir or files should be specified
- bucketSize must be a power of two (from 1 up to 128) but…
- Cannot have a null/empty columns
- Cannot set kafka property [auto.offset.reset]. Property…
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)