apache/pulsar · error · NegativeArraySizeException

nbits < 0: <nbits>

Error message

nbits < 0: <nbits>

What it means

BitSetRecyclable(int nbits) mirrors java.util.BitSet's constructor: a negative initial bit count is rejected with NegativeArraySizeException("nbits < 0: " + nbits) before any words are allocated. The library throws this to fail fast rather than letting an internal long[] allocation fail confusingly, since a negative length array is impossible. A size of 0 is explicitly allowed.

Source

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

     */
    public BitSetRecyclable() {
        initWords(BITS_PER_WORD);
        sizeIsSticky = false;
    }

    /**
     * Creates a bit set whose initial size is large enough to explicitly
     * represent bits with indices in the range {@code 0} through
     * {@code nbits-1}. All bits are initially {@code false}.
     *
     * @param  nbits the initial size of the bit set
     * @throws NegativeArraySizeException if the specified initial size
     *         is negative
     */
    public BitSetRecyclable(int nbits) {
        // nbits can't be negative; size 0 is OK
        if (nbits < 0)
            throw new NegativeArraySizeException("nbits < 0: " + nbits);

        initWords(nbits);
        sizeIsSticky = true;
    }

    private void initWords(int nbits) {
        words = new long[wordIndex(nbits-1) + 1];
    }

    /**
     * Creates a bit set using words as the internal representation.
     * The last word (if there is one) must be non-zero.
     */
    private BitSetRecyclable(long[] words) {
        this.words = words;
        this.wordsInUse = words.length;
        checkInvariants();
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Clamp or reject before construction: if (n < 0) n = 0; or throw a domain-specific error naming the source of n.
  2. If n is a sentinel for 'not set', branch on it explicitly instead of passing it to the constructor.
  3. Check the arithmetic producing n for overflow (use Math.addExact/Math.multiplyExact) and for integer division/cast errors.
  4. If the value comes from deserialized data, validate the length header against remaining bytes before trusting it.

Example fix

// before
int nbits = header.getLength(); // may be -1 sentinel
BitSetRecyclable set = new BitSetRecyclable(nbits);

// after
int nbits = header.getLength();
if (nbits < 0) nbits = 0; // treat sentinel/invalid as empty bit set
BitSetRecyclable set = new BitSetRecyclable(nbits);
Defensive patterns

Strategy: validation

Validate before calling

static BitSetRecyclable newBitSet(int nbits) {
    if (nbits < 0) {
        throw new IllegalArgumentException("nbits must be >= 0, got " + nbits + " (check sentinel/overflow)");
    }
    return new BitSetRecyclable(nbits);
}

Type guard

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

Try / catch

try {
    BitSetRecyclable set = new BitSetRecyclable(nbits);
} catch (NegativeArraySizeException e) {
    log.warn("Non-positive bit-set size requested: {}", e.getMessage());
    BitSetRecyclable set = new BitSetRecyclable(0); // or rethrow as a data-corruption error
}

Prevention

When it happens

Trigger: Constructing new BitSetRecyclable(n) where n < 0, typically when n comes from a computation such as (numBits/8 + 1)*8 gone wrong, an unchecked cast, a corrupted length header, or a recycled/recyclable value that was reset to -1 as a sentinel and passed straight to the constructor.

Common situations: Deserializing a payload whose declared bit-set length is negative (corrupt or hostile data); arithmetic overflow wrapping a large positive count to negative; calling the constructor inside a recycling path where a sentinel value (-1 meaning 'uninitialized') leaks through; off-by-one/negative results from subtracting sizes.

Related errors


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