apache/pulsar · error · IndexOutOfBoundsException
fromIndex < -1: <fromIndex>
Error message
fromIndex < -1: <fromIndex>
What it means
BitSetRecyclable.previousSetBit(int fromIndex) returns the nearest set bit at or before fromIndex; it deliberately allows fromIndex == -1 (returns -1 as the exhausted sentinel) but throws IndexOutOfBoundsException for any index < -1. This mirrors java.util.BitSet's JDK 1.7 semantics, so only genuinely corrupted cursors (<= -2) trigger the throw.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/collections/BitSetRecyclable.java:765
* use the following loop:
*
* <pre> {@code
* for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {
* // operate on index i here
* }}</pre>
*
* @param fromIndex the index to start checking from (inclusive)
* @return the index of the previous set bit, or {@code -1} if there
* is no such bit
* @throws IndexOutOfBoundsException if the specified index is less
* than {@code -1}
* @since 1.7
*/
public int previousSetBit(int fromIndex) {
if (fromIndex < 0) {
if (fromIndex == -1)
return -1;
throw new IndexOutOfBoundsException(
"fromIndex < -1: " + fromIndex);
}
checkInvariants();
int u = wordIndex(fromIndex);
if (u >= wordsInUse)
return length() - 1;
long word = words[u] & (WORD_MASK >>> -(fromIndex+1));
while (true) {
if (word != 0)
return (u+1) * BITS_PER_WORD - 1 - Long.numberOfLeadingZeros(word);
if (u-- == 0)
return -1;
word = words[u];
}View on GitHub (pinned to 820761864e)
Solutions
- Stop backward iteration when previousSetBit returns -1
- Guard: only call when fromIndex >= -1
- Fix the cursor arithmetic that produced values below -1
- Add a boundary test starting previousSetBit from 0 and walking down
Example fix
// before int prev = bs.previousSetBit(cursor); // cursor may be < -1 // after int prev = cursor < -1 ? -1 : bs.previousSetBit(cursor); if (prev < 0) break;
Defensive patterns
Strategy: validation
Validate before calling
if (fromIndex < -1) {
throw new IllegalArgumentException("fromIndex must be >= -1, got " + fromIndex);
}
int prev = ackSet.previousSetBit(fromIndex); Type guard
static boolean isBackwardScanStart(int idx) { return idx >= -1; } Try / catch
for (int i = ackSet.previousSetBit(start); i >= 0; i = ackSet.previousSetBit(i - 1)) {
// process i
} // -1 terminates naturally and is a legal input Prevention
- Stop backward loops at -1; note -1 itself is legal input here
- Avoid decrementing cursors that already reached -1
- Centralize backward-scan loops in one utility
- Boundary-test scans starting at 0
When it happens
Trigger: Calling previousSetBit with an index <= -2, e.g. continuing a backward loop past the -1 sentinel, or passing an uninitialized/error value like Integer.MIN_VALUE or a wrapped-around subtraction result.
Common situations: Backward scans over ack sets where the loop doesn't stop at -1; index arithmetic like idx-2 from a 0 start; using an error-code return as the next cursor.
Related errors
- fromIndex < 0: <fromIndex>
- toIndex < 0: <toIndex>
- fromIndex: <fromIndex> > toIndex: <toIndex>
- bitIndex < 0: <bitIndex>
- nbits < 0: <nbits>
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/b6d6f24ae1dae84a.
Report an issue: GitHub.