apache/druid · error · IllegalArgumentException
Cannot deserialize type[%s] to an RoaringBitmap64Counter:
Error message
Cannot deserialize type[%s] to an RoaringBitmap64Counter:
What it means
deserializeRoaringBitmap64Counter accepts only String (base64-encoded), byte[], and RoaringBitmap64Counter values; anything else (null included) triggers IllegalArgumentException("Cannot deserialize type[%s] to an RoaringBitmap64Counter:"). Note the message has a formatting bug: %s is present but no argument is substituted after the class name, so the message ends with a colon. It indicates the input value's Java type is not one of the supported representations of a RoaringBitmap64Counter.
Source
Thrown at extensions-contrib/druid-exact-count-bitmap/src/main/java/org/apache/druid/query/aggregation/exact/count/bitmap64/Bitmap64ExactCountMergeComplexMetricSerde.java:48
import org.apache.druid.segment.serde.ComplexMetricExtractor;
import org.apache.druid.segment.serde.ComplexMetricSerde;
import org.apache.druid.segment.serde.LargeColumnSupportedComplexColumnSerializer;
import org.apache.druid.segment.writeout.SegmentWriteOutMedium;
import java.nio.ByteBuffer;
public class Bitmap64ExactCountMergeComplexMetricSerde extends ComplexMetricSerde
{
static RoaringBitmap64Counter deserializeRoaringBitmap64Counter(final Object object)
{
if (object instanceof String) {
return RoaringBitmap64Counter.fromBytes(decodeStringToByteArray((String) object));
} else if (object instanceof byte[]) {
return RoaringBitmap64Counter.fromBytes((byte[]) object);
} else if (object instanceof RoaringBitmap64Counter) {
return (RoaringBitmap64Counter) object;
}
throw new IAE("Cannot deserialize type[%s] to an RoaringBitmap64Counter:", object.getClass().getName());
}
private static byte[] decodeStringToByteArray(String string)
{
try {
return StringUtils.decodeBase64(StringUtils.toUtf8(string));
}
catch (IllegalArgumentException e) {
throw new IAE("Failed to deserialize to RoaringBitmap64Counter, input is an invalid base64 string");
}
}
@Override
public String getTypeName()
{
return Bitmap64ExactCountModule.TYPE_NAME; // must be common type name
}
View on GitHub (pinned to 9b90983fd2)
Solutions
- Inspect the actual Java class logged in the error and convert it before deserialization: pass either a base64 String, a byte[], or an already-built RoaringBitmap64Counter.
- If the value arrives as a ByteBuffer, copy it out: byte[] bytes = new byte[buf.remaining()]; buf.get(bytes);
- If the value is null, guard it in your extraction/parsing code and skip or default the metric rather than passing null to the serde.
- If a connector/parser is producing the wrong type, add an input transform (flattenJSON / deserialize step) to normalize the column to a base64 string.
- Report/patch the message formatting bug (the %s placeholder is never filled) if you maintain a fork.
Example fix
// before
Object v = row.getRaw(column);
RoaringBitmap64Counter c = serde.extractValue(v); // may be ByteBuffer
// after
Object v = row.getRaw(column);
if (v instanceof ByteBuffer) {
ByteBuffer b = (ByteBuffer) v;
byte[] bytes = new byte[b.remaining()];
b.get(bytes);
v = bytes;
}
RoaringBitmap64Counter c = serde.extractValue(v); Defensive patterns
Strategy: type-guard
Validate before calling
if (value == null) return null; // or default counter
if (!(value instanceof String || value instanceof byte[] || value instanceof RoaringBitmap64Counter)) {
throw new IllegalArgumentException("Unsupported bitmap value type: " + value.getClass());
} Type guard
static boolean isDeserializableBitmap(Object o) {
return o instanceof RoaringBitmap64Counter
|| o instanceof byte[]
|| o instanceof String;
} Try / catch
try {
RoaringBitmap64Counter c = serde.extractValue(rawValue);
} catch (IllegalArgumentException e) {
// normalize the raw value (ByteBuffer -> byte[], null -> skip) and retry once
} Prevention
- Normalize the bitmap column to a base64 String at the parser/transform layer before it reaches the serde.
- Log value.getClass() when a serde error occurs to catch connector type changes early.
- Add ingestion-time validation tests for each input format (JSON, Avro, Kafka) you feed to bitmap64 columns.
When it happens
Trigger: Calling extractValue / deserializeRoaringBitmap64Counter with an object that is not a String, byte[], or RoaringBitmap64Counter — e.g. a ByteBuffer, List, Map, or null returned by the input layer for a BITMAP64_EXACT_COUNT column.
Common situations: Ingesting from a source (JSON/Avro/Parquet/Kafka) where the bitmap metric column arrives as a non-string structured type; a Druid version or parser change altering the deserialized Java type; query-time lookups handing back native objects; feeding raw serialized payloads through the wrong serde.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Expected a number or an instance of MergingDigest, but recei
- Object is not of a type that can be deserialized to a quanti
- Object is not of a type that can be deserialized to a KllFlo
- Object is not of a type that can be deserialized to a quanti
- Object is not of a type that can deserialize to sketch: %s
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/4e6582775632449d.
Report an issue: GitHub.