apache/pulsar · error · RuntimeException

Failed to deserialize LongBitmap

Error message

Failed to deserialize LongBitmap

What it means

ConcurrentRoaringBitmap.deserialize wraps IOException from the underlying RoaringBitmap deserialization in a RuntimeException('Failed to deserialize LongBitmap'). It indicates the byte buffer did not contain a valid serialized roaring bitmap.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/collections/ConcurrentRoaringBitmap.java:441

            bitmap.clear();
            RoaringBitmap rb = BitSetUtil.bitmapOf(data);
            bitmap.or(rb.toMutableRoaringBitmap());
            removesSinceTrim = 0;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    static ConcurrentRoaringBitmap deserialize(ByteBuf buf) {
        try {
            ByteBuffer nioBuffer = buf.nioBuffer(buf.readerIndex(), buf.readableBytes());
            int startPosition = nioBuffer.position();
            MutableRoaringBitmap bitmap = new MutableRoaringBitmap();
            bitmap.deserialize(new ByteBufferDataInput(nioBuffer));
            buf.skipBytes(nioBuffer.position() - startPosition);
            return new ConcurrentRoaringBitmap(bitmap);
        } catch (IOException e) {
            throw new RuntimeException("Failed to deserialize LongBitmap", e);
        }
    }

    /**
     * Trims the underlying bitmap if enough removals have accumulated or it's empty.
     * Caller must hold the write lock and have already updated {@link #removesSinceTrim}.
     */
    private void maybeTrim() {
        if (removesSinceTrim >= TRIM_AFTER_REMOVES || bitmap.isEmpty()) {
            bitmap.trim();
            removesSinceTrim = 0;
        }
    }

    private static void validateRange(long value) {
        if (value < 0 || value > MAX_UINT32) {
            throw new IllegalArgumentException(
                    "Value out of range [0, " + MAX_UINT32 + "]: " + value);

View on GitHub (pinned to 820761864e)

Solutions

  1. Verify the bytes were produced by ConcurrentRoaringBitmap.serialize and are complete/untruncated
  2. Check buffer reader/writer indices and offsets; deserialize expects the bitmap at the current position
  3. Catch the RuntimeException and treat the payload as corrupt (rebuild from source data or skip)

Example fix

// before
ConcurrentRoaringBitmap bm = ConcurrentRoaringBitmap.deserialize(buf);
// after
try {
    ConcurrentRoaringBitmap bm = ConcurrentRoaringBitmap.deserialize(buf);
} catch (RuntimeException e) {
    log.warn("Corrupt LongBitmap payload", e);
    bm = new ConcurrentRoaringBitmap();
}
Defensive patterns

Strategy: try-catch

Validate before calling

static boolean looksLikeBitmap(ByteBuf buf) {
    return buf != null && buf.readableBytes() > 0; // full validity only verifiable by deserializing
}

Try / catch

try {
    ConcurrentRoaringBitmap bm = ConcurrentRoaringBitmap.deserialize(buf);
} catch (RuntimeException e) {
    // corrupted or foreign-format payload; rebuild or skip
    log.warn("Failed to deserialize LongBitmap", e);
}

Prevention

When it happens

Trigger: Calling deserialize(ByteBuf/ByteBuffer) whose bytes are truncated, corrupted, produced by a different serialization format, or at the wrong offset.

Common situations: Version/format changes between writer and reader, corrupt persistence or wire transfer, misaligned buffer positions when reading a stream of serialized bitmaps.

Related errors


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