{"record":{"id":"b6d6f24ae1dae84a","repo":"apache/pulsar","slug":"fromindex-1-fromindex","errorCode":null,"errorMessage":"fromIndex < -1: <fromIndex>","messagePattern":"fromIndex < -1: <fromIndex>","errorType":"validation","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/util/collections/BitSetRecyclable.java","lineNumber":765,"sourceCode":"     * use the following loop:\n     *\n     *  <pre> {@code\n     * for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {\n     *     // operate on index i here\n     * }}</pre>\n     *\n     * @param  fromIndex the index to start checking from (inclusive)\n     * @return the index of the previous set bit, or {@code -1} if there\n     *         is no such bit\n     * @throws IndexOutOfBoundsException if the specified index is less\n     *         than {@code -1}\n     * @since  1.7\n     */\n    public int previousSetBit(int fromIndex) {\n        if (fromIndex < 0) {\n            if (fromIndex == -1)\n                return -1;\n            throw new IndexOutOfBoundsException(\n                \"fromIndex < -1: \" + fromIndex);\n        }\n\n        checkInvariants();\n\n        int u = wordIndex(fromIndex);\n        if (u >= wordsInUse)\n            return length() - 1;\n\n        long word = words[u] & (WORD_MASK >>> -(fromIndex+1));\n\n        while (true) {\n            if (word != 0)\n                return (u+1) * BITS_PER_WORD - 1 - Long.numberOfLeadingZeros(word);\n            if (u-- == 0)\n                return -1;\n            word = words[u];\n        }","sourceCodeStart":747,"sourceCodeEnd":783,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/util/collections/BitSetRecyclable.java#L747-L783","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nint prev = bs.previousSetBit(cursor); // cursor may be < -1\n// after\nint prev = cursor < -1 ? -1 : bs.previousSetBit(cursor);\nif (prev < 0) break;","handlingStrategy":"validation","validationCode":"if (fromIndex < -1) {\n    throw new IllegalArgumentException(\"fromIndex must be >= -1, got \" + fromIndex);\n}\nint prev = ackSet.previousSetBit(fromIndex);","typeGuard":"static boolean isBackwardScanStart(int idx) { return idx >= -1; }","tryCatchPattern":"for (int i = ackSet.previousSetBit(start); i >= 0; i = ackSet.previousSetBit(i - 1)) {\n    // process i\n} // -1 terminates naturally and is a legal input","preventionTips":["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"],"tags":["java","bitset","index-out-of-bounds","iteration"],"backgroundTag":"index-out-of-bounds","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}