{"record":{"id":"4e6582775632449d","repo":"apache/druid","slug":"cannot-deserialize-type-s-to-an-roaringbitmap64c","errorCode":null,"errorMessage":"Cannot deserialize type[%s] to an RoaringBitmap64Counter:","messagePattern":"Cannot deserialize type\\[(.+?)\\] to an RoaringBitmap64Counter:","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions-contrib/druid-exact-count-bitmap/src/main/java/org/apache/druid/query/aggregation/exact/count/bitmap64/Bitmap64ExactCountMergeComplexMetricSerde.java","lineNumber":48,"sourceCode":"import org.apache.druid.segment.serde.ComplexMetricExtractor;\nimport org.apache.druid.segment.serde.ComplexMetricSerde;\nimport org.apache.druid.segment.serde.LargeColumnSupportedComplexColumnSerializer;\nimport org.apache.druid.segment.writeout.SegmentWriteOutMedium;\n\nimport java.nio.ByteBuffer;\n\npublic class Bitmap64ExactCountMergeComplexMetricSerde extends ComplexMetricSerde\n{\n  static RoaringBitmap64Counter deserializeRoaringBitmap64Counter(final Object object)\n  {\n    if (object instanceof String) {\n      return RoaringBitmap64Counter.fromBytes(decodeStringToByteArray((String) object));\n    } else if (object instanceof byte[]) {\n      return RoaringBitmap64Counter.fromBytes((byte[]) object);\n    } else if (object instanceof RoaringBitmap64Counter) {\n      return (RoaringBitmap64Counter) object;\n    }\n    throw new IAE(\"Cannot deserialize type[%s] to an RoaringBitmap64Counter:\", object.getClass().getName());\n  }\n\n  private static byte[] decodeStringToByteArray(String string)\n  {\n    try {\n      return StringUtils.decodeBase64(StringUtils.toUtf8(string));\n    }\n    catch (IllegalArgumentException e) {\n      throw new IAE(\"Failed to deserialize to RoaringBitmap64Counter, input is an invalid base64 string\");\n    }\n  }\n\n  @Override\n  public String getTypeName()\n  {\n    return Bitmap64ExactCountModule.TYPE_NAME; // must be common type name\n  }\n","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-contrib/druid-exact-count-bitmap/src/main/java/org/apache/druid/query/aggregation/exact/count/bitmap64/Bitmap64ExactCountMergeComplexMetricSerde.java#L30-L66","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nObject v = row.getRaw(column);\nRoaringBitmap64Counter c = serde.extractValue(v); // may be ByteBuffer\n// after\nObject v = row.getRaw(column);\nif (v instanceof ByteBuffer) {\n  ByteBuffer b = (ByteBuffer) v;\n  byte[] bytes = new byte[b.remaining()];\n  b.get(bytes);\n  v = bytes;\n}\nRoaringBitmap64Counter c = serde.extractValue(v);","handlingStrategy":"type-guard","validationCode":"if (value == null) return null; // or default counter\nif (!(value instanceof String || value instanceof byte[] || value instanceof RoaringBitmap64Counter)) {\n  throw new IllegalArgumentException(\"Unsupported bitmap value type: \" + value.getClass());\n}","typeGuard":"static boolean isDeserializableBitmap(Object o) {\n  return o instanceof RoaringBitmap64Counter\n      || o instanceof byte[]\n      || o instanceof String;\n}","tryCatchPattern":"try {\n  RoaringBitmap64Counter c = serde.extractValue(rawValue);\n} catch (IllegalArgumentException e) {\n  // normalize the raw value (ByteBuffer -> byte[], null -> skip) and retry once\n}","preventionTips":["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."],"tags":["deserialization","type-mismatch","java","druid"],"backgroundTag":"incompatible-source-type","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}