apache/pulsar · error · IndexOutOfBoundsException

fromIndex: <fromIndex> > toIndex: <toIndex>

Error message

fromIndex: <fromIndex> > toIndex: <toIndex>

What it means

BitSetRecyclable.checkRange validates index ranges for the range-based flip(int,int), set(int,int) and clear(int,int) methods (and the get(int,int) read path). When fromIndex is greater than toIndex the range is empty/malformed, so the method throws IndexOutOfBoundsException instead of silently doing nothing. It is a caller-contract violation mirroring java.util.BitSet semantics.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/collections/BitSetRecyclable.java:343

     */
    private void expandTo(int wordIndex) {
        int wordsRequired = wordIndex+1;
        if (wordsInUse < wordsRequired) {
            ensureCapacity(wordsRequired);
            wordsInUse = wordsRequired;
        }
    }

    /**
     * Checks that fromIndex ... toIndex is a valid range of bit indices.
     */
    private static void checkRange(int fromIndex, int toIndex) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
        if (toIndex < 0)
            throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex);
        if (fromIndex > toIndex)
            throw new IndexOutOfBoundsException("fromIndex: " + fromIndex +
                " > toIndex: " + toIndex);
    }

    /**
     * Sets the bit at the specified index to the complement of its
     * current value.
     *
     * @param  bitIndex the index of the bit to flip
     * @throws IndexOutOfBoundsException if the specified index is negative
     * @since  1.4
     */
    public void flip(int bitIndex) {
        if (bitIndex < 0)
            throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

        int wordIndex = wordIndex(bitIndex);
        expandTo(wordIndex);

View on GitHub (pinned to 820761864e)

Solutions

  1. Check the call site and ensure arguments are passed as (fromIndex, toIndex) with fromIndex <= toIndex
  2. Guard or normalize the range before calling: fromIndex = Math.min(a,b); toIndex = Math.max(a,b)
  3. Skip the call when the range is empty (fromIndex == toIndex only clears/sets nothing for ranges) or log the source of the inverted range
  4. Add a unit test covering inverted ranges if the indices come from user/protocol data

Example fix

// before
bitSet.flip(endIndex, startIndex);
// after
if (startIndex <= endIndex) {
    bitSet.flip(startIndex, endIndex);
}
Defensive patterns

Strategy: validation

Validate before calling

if (fromIndex < 0 || toIndex < 0) throw new IllegalArgumentException("negative index");
if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex " + fromIndex + " > toIndex " + toIndex);

Try / catch

try {
    bitSet.set(fromIndex, toIndex);
} catch (IndexOutOfBoundsException e) {
    LOG.warn("invalid range [{}, {}]", fromIndex, toIndex, e);
}

Prevention

When it happens

Trigger: Calling BitSetRecyclable.flip(int,int), set(int,int), clear(int,int) or get(int,int) with fromIndex > toIndex, e.g. after swapping variables, computing indices from min/max incorrectly, or building a range from user input where the start can exceed the end.

Common situations: Batch-acknowledgment code computing message ranges from data where the range is inverted (end before start after sorting changes); off-by-one loops passing (i, j) in the wrong order; refactoring a single-bit call into a range call and swapping arguments.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/540e879d3f83d548. Report an issue: GitHub.