apache/druid · error · IllegalArgumentException

Size must be nonnegative

Error message

Size must be nonnegative

What it means

FrameProcessors.rangeSet(size) builds the IntSortedSet {0..size-1} representing channel indexes. A negative size is meaningless, so it throws IAE immediately. This helper is typically used to iterate over all channels of a stage or outputs of a processor.

Solutions

  1. Clamp the computed size to zero before calling: rangeSet(Math.max(0, n)).
  2. Fix the arithmetic that produced the negative count (often an off-by-one on an empty collection).
  3. Validate upstream that the channel/collection count is nonnegative before generating indexes.

Example fix

// before
IntSortedSet set = FrameProcessors.rangeSet(channels.size() - removed);
// after
IntSortedSet set = FrameProcessors.rangeSet(Math.max(0, channels.size() - removed));
Defensive patterns

Strategy: validation

Validate before calling

if (size < 0) {
  throw new IllegalArgumentException("rangeSet size must be >= 0, got " + size);
}

Type guard

boolean validSize(int n) { return n >= 0; }

Try / catch

try {
  IntSortedSet s = FrameProcessors.rangeSet(n);
} catch (IllegalArgumentException e) {
  // clamp or fix the computed count
}

Prevention

When it happens

Trigger: Calling `FrameProcessors.rangeSet(-1)` directly, or indirectly via code that computes the count as `channels.size() - skipped` or `n - 1` where n is 0, producing a negative number.

Common situations: Off-by-one arithmetic on empty channel lists; subtracting a removal count that exceeds list size; miscomputed stage input counts during distributed query planning.

Related errors


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

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/frame/processor/FrameProcessors.java:134

                                                           .setVirtualColumns(virtualColumns)
                                                           .build();
    // Despite appearances of columnar FrameCursorHolderFactory with its closers, it is currently safe to never close
    // the CursorHolder that the FrameCursor comes from because it really does nothing. The row based
    // FrameCursorHolderFactory has no closer stuff at all and is totally safe. If this ever changes, this method will
    // probably need to wrap the cursor in something closeable, or be reworked to just return the CursorHolder so that
    // callers can deal with closing the stuff.
    return (FrameCursor) frameReader.makeCursorFactory(frame).makeCursorHolder(cursorBuildSpec).asCursor();
  }

  /**
   * Creates a mutable sorted set from 0 to "size" (exclusive).
   *
   * @throws IllegalArgumentException if size is negative
   */
  public static IntSortedSet rangeSet(final int size)
  {
    if (size < 0) {
      throw new IAE("Size must be nonnegative");
    }

    final IntSortedSet set = new IntAVLTreeSet();

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

    return set;
  }

  /**
   * Selects a random element from a set of ints.
   */
  public static int selectRandom(final IntSet ints)
  {
    final int idx = ThreadLocalRandom.current().nextInt(ints.size());
    final IntIterator iterator = ints.iterator();

View on GitHub (pinned to 9b90983fd2)