elastic/elasticsearch · error · IllegalArgumentException

[bitmap_terms] query value is not a valid serialized Roaring

Error message

[bitmap_terms] query value is not a valid serialized RoaringBitmap

What it means

Thrown by integerValues when IntBitmap.deserialize fails on the decoded bytes. The bytes were valid base64 but do not form a valid serialized 32-bit RoaringBitmap (wrong cookie/header, truncated, or a 64-bit portable-format payload supplied to an integer field).

Source

Thrown at modules/bitmap/src/main/java/org/elasticsearch/index/query/bitmapterms/BitmapTermsQueryBuilder.java:186

        BitmapValues values = switch (numberFieldType.numberType()) {
            case INTEGER -> integerValues(bitmapBytes);
            case LONG -> longValues(bitmapBytes);
            default -> throw new AssertionError("unexpected number type [" + numberFieldType.numberType() + "]");
        };
        // The two queries differ only in which index structure they merge against; the field's width
        // is carried by the BitmapValues.
        if (numberFieldType.isIndexedWithTerms()) {
            return new BitmapTermsQuery(fieldName, values);
        }
        return new BitmapBKDQuery(fieldName, values);
    }

    private static IntBitmap integerValues(byte[] bitmapBytes) {
        IntBitmap bitmap;
        try {
            bitmap = IntBitmap.deserialize(bitmapBytes);
        } catch (Exception e) {
            throw new IllegalArgumentException("[bitmap_terms] query value is not a valid serialized RoaringBitmap", e);
        }
        if (bitmap.hasNegativeValues()) {
            throw new IllegalArgumentException(
                "[bitmap_terms] query on [integer] field only supports non-negative values (0 to 2147483647)"
            );
        }
        return bitmap;
    }

    private static LongBitmap longValues(byte[] bitmapBytes) {
        LongBitmap bitmap;
        try {
            bitmap = LongBitmap.deserializePortable(bitmapBytes);
        } catch (Exception e) {
            throw new IllegalArgumentException(
                "[bitmap_terms] query value is not a valid serialized 64-bit RoaringBitmap in the portable format",
                e
            );

View on GitHub (pinned to db6a809a66)

Solutions

  1. For integer fields, serialize with 32-bit RoaringBitmap.serialize (java) or pyroaching BitMap.serialize, then base64-encode.
  2. Do not feed a 64-bit/portable-format payload to an integer field; use a long field for 64-bit bitmaps.
  3. Verify the byte length and cookie header match the 32-bit roaring format before sending.

Example fix

// before: 64-bit portable bytes sent to integer field
bitmap = Roaring64NavigableMap(); bitmap.add(...); value = base64(bitmap.serializePortable())
// after: 32-bit bitmap for integer field
bitmap = RoaringBitmap(); bitmap.add(...); value = base64(bitmap.serialize())
Defensive patterns

Strategy: validation

Validate before calling

// Round-trip the bitmap on the client to ensure it deserializes before sending.
import org.roaringbitmap.RoaringBitmap;
byte[] bytes = ...; // your serialized 32-bit bitmap
RoaringBitmap r = RoaringBitmap.deserialize(ByteBuffer.wrap(bytes));
if (r.hasNegativeValuesRunningLengthIntervals()) throw new IllegalArgumentException("invalid");
String value = Base64.getEncoder().encodeToString(bytes);

Prevention

When it happens

Trigger: Supplying a Roaring64NavigableMap.serializePortable payload to an integer field; supplying a portable vs run-length-laid-out 32-bit bitmap that the deserializer rejects; truncated bytes from a wrong encoder. Note the integer path expects the format from RoaringBitmap.serialize / pyroaring BitMap.serialize.

Common situations: Reusing the same base64 blob across integer and long fields; client using the wrong Roaring serialization method; version skew between the Roaring library on the client and server.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/cb19a176e70a6fa1. Report an issue: GitHub.