{"record":{"id":"5e9ccd6cb1a27c82","repo":"apache/druid","slug":"size-must-be-nonnegative","errorCode":null,"errorMessage":"Size must be nonnegative","messagePattern":"Size must be nonnegative","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/FrameProcessors.java","lineNumber":134,"sourceCode":"                                                           .setVirtualColumns(virtualColumns)\n                                                           .build();\n    // Despite appearances of columnar FrameCursorHolderFactory with its closers, it is currently safe to never close\n    // the CursorHolder that the FrameCursor comes from because it really does nothing. The row based\n    // FrameCursorHolderFactory has no closer stuff at all and is totally safe. If this ever changes, this method will\n    // probably need to wrap the cursor in something closeable, or be reworked to just return the CursorHolder so that\n    // callers can deal with closing the stuff.\n    return (FrameCursor) frameReader.makeCursorFactory(frame).makeCursorHolder(cursorBuildSpec).asCursor();\n  }\n\n  /**\n   * Creates a mutable sorted set from 0 to \"size\" (exclusive).\n   *\n   * @throws IllegalArgumentException if size is negative\n   */\n  public static IntSortedSet rangeSet(final int size)\n  {\n    if (size < 0) {\n      throw new IAE(\"Size must be nonnegative\");\n    }\n\n    final IntSortedSet set = new IntAVLTreeSet();\n\n    for (int i = 0; i < size; i++) {\n      set.add(i);\n    }\n\n    return set;\n  }\n\n  /**\n   * Selects a random element from a set of ints.\n   */\n  public static int selectRandom(final IntSet ints)\n  {\n    final int idx = ThreadLocalRandom.current().nextInt(ints.size());\n    final IntIterator iterator = ints.iterator();","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/FrameProcessors.java#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"Off-by-one arithmetic on empty channel lists; subtracting a removal count that exceeds list size; miscomputed stage input counts during distributed query planning.","solutions":["Clamp the computed size to zero before calling: rangeSet(Math.max(0, n)).","Fix the arithmetic that produced the negative count (often an off-by-one on an empty collection).","Validate upstream that the channel/collection count is nonnegative before generating indexes."],"exampleFix":"// before\nIntSortedSet set = FrameProcessors.rangeSet(channels.size() - removed);\n// after\nIntSortedSet set = FrameProcessors.rangeSet(Math.max(0, channels.size() - removed));","handlingStrategy":"validation","validationCode":"if (size < 0) {\n  throw new IllegalArgumentException(\"rangeSet size must be >= 0, got \" + size);\n}","typeGuard":"boolean validSize(int n) { return n >= 0; }","tryCatchPattern":"try {\n  IntSortedSet s = FrameProcessors.rangeSet(n);\n} catch (IllegalArgumentException e) {\n  // clamp or fix the computed count\n}","preventionTips":["Clamp computed sizes with Math.max(0, n).","Watch for size - removal arithmetic on possibly-empty collections.","Unit-test counts for empty inputs."],"tags":["java","argument-validation","off-by-one"],"backgroundTag":"argument-out-of-range","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"}