apache/pulsar · error · IllegalArgumentException

Unsupported LongBitmap type: <other.getClass()>

Error message

Unsupported LongBitmap type: <other.getClass()>

What it means

ConcurrentRoaringBitmap.or(LongBitmap) only supports bitwise-OR with another ConcurrentRoaringBitmap; passing any other LongBitmap implementation throws IllegalArgumentException. Locking order for the merge is only defined between instances of this class.

Source

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

        // ensures rank(0x100000000L) counts all values in the uint32 range.
        if (value > UINT32_SIZE) {
            value = UINT32_SIZE;
        }
        long stamp = lock.readLock();
        try {
            return bitmap.rank((int) (value - 1));
        } finally {
            lock.unlockRead(stamp);
        }
    }

    @Override
    public void or(LongBitmap other) {
        if (other == this) {
            return;
        }
        if (!(other instanceof ConcurrentRoaringBitmap)) {
            throw new IllegalArgumentException("Unsupported LongBitmap type: " + other.getClass());
        }
        ConcurrentRoaringBitmap that = (ConcurrentRoaringBitmap) other;

        // Acquire this.writeLock + that.readLock in identityHashCode order so concurrent
        // A.or(B) and B.or(A) don't deadlock. Fall back to inner bitmap identity on collision.
        boolean thisFirst;
        int outerCmp = Integer.compare(
                System.identityHashCode(this), System.identityHashCode(that));
        if (outerCmp != 0) {
            thisFirst = outerCmp < 0;
        } else {
            thisFirst = System.identityHashCode(this.bitmap) < System.identityHashCode(that.bitmap);
        }

        if (thisFirst) {
            long thisStamp = this.lock.writeLock();
            try {
                long thatStamp = that.lock.readLock();

View on GitHub (pinned to 820761864e)

Solutions

  1. Ensure both operands are ConcurrentRoaringBitmap instances; copy foreign bitmaps via deserialization into a ConcurrentRoaringBitmap first
  2. Guard with `other instanceof ConcurrentRoaringBitmap` before calling or()
  3. Standardize on one LongBitmap implementation across the codebase

Example fix

// before
bitmap.or(foreignBitmap); // throws
// after
if (foreignBitmap instanceof ConcurrentRoaringBitmap) {
    bitmap.or(foreignBitmap);
} else {
    ConcurrentRoaringBitmap converted =
        ConcurrentRoaringBitmap.deserialize(foreignBitmap.serialize());
    bitmap.or(converted);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(other instanceof ConcurrentRoaringBitmap)) {
    other = ConcurrentRoaringBitmap.deserialize(other.serialize());
}
bm.or(other);

Type guard

static boolean isConcurrentRoaring(LongBitmap b) {
    return b instanceof ConcurrentRoaringBitmap;
}

Try / catch

try {
    bm.or(other);
} catch (IllegalArgumentException e) {
    throw new IllegalStateException("Mixing LongBitmap implementations: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Calling bitmap.or(otherBitmap) where otherBitmap is a foreign LongBitmap implementation (custom class or a different library's roaring bitmap wrapper).

Common situations: Mixing bitmap implementations across plugins/modules, or after a library swap where a different LongBitmap impl is returned by an API.

Related errors


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