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
- Verify the bytes were produced by ConcurrentRoaringBitmap.serialize and are complete/untruncated
- Check buffer reader/writer indices and offsets; deserialize expects the bitmap at the current position
- 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
- Keep serialization format and library versions in sync across writers/readers
- Frame serialized bitmaps with length prefixes to detect truncation
- Persist a checksum alongside serialized bitmaps
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
- Unexpected metadata type: ${className}
- Rejected legacy package metadata:
- ${key} already exists in the dynamicConfigurationMap
- Topic factory failed to create topic
- No more range can assigned to new consumer, assigned consume
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/74109b2edac4267b.
Report an issue: GitHub.