apache/druid · error · IllegalArgumentException
Cannot add / to a Spectator Histogram
Error message
Cannot add / to a Spectator Histogram
What it means
The private add(Object key, Number value) overload accepts either a Number (treated as a bucket index) or a byte[] (lookup key); anything else is unsupported. When the key is neither, the library throws IAE because it cannot interpret the entry as a histogram bucket. This typically fires during deserialization of a map with unexpected key types.
Source
Thrown at extensions-contrib/spectator-histogram/src/main/java/org/apache/druid/spectator/histogram/SpectatorHistogram.java:321
{
if (bucket >= PercentileBuckets.length() || bucket < 0) {
throw new IAE("Bucket index out of range (0, " + PercentileBuckets.length() + ")");
}
writableMap().addTo((short) bucket, count);
this.sumOfCounts += count;
}
private void add(Object key, Number value)
{
if (key instanceof String) {
this.add(Integer.parseInt((String) key), value.longValue());
return;
}
if (Number.class.isAssignableFrom(key.getClass())) {
this.add(((Number) key).intValue(), value.longValue());
return;
}
throw new IAE(
"Cannot add " + key.getClass() + "/" + value.getClass() + " to a Spectator Histogram"
);
}
// Used for testing
long get(int idx)
{
return readableMap().get((short) idx);
}
// Accessible for serialization
void serialize(JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException
{
JacksonUtils.writeObjectUsingSerializerProvider(jsonGenerator, serializerProvider, readableMap());
}
public boolean isEmpty()
{View on GitHub (pinned to 9b90983fd2)
Solutions
- Ensure the map passed to deserialize has numeric keys (produce it via SpectatorHistogram.serialize).
- Convert String keys to integers/bucket indices before deserialization.
- If ingesting JSON, use a serializer that preserves integer keys rather than coercing them to strings.
Example fix
// before Map<String, Long> m = readJson(); SpectatorHistogram h = SpectatorHistogram.deserialize(m); // after Map<Integer, Long> m = readJson(); // integer keys preserved SpectatorHistogram h = SpectatorHistogram.deserialize(m);
Defensive patterns
Strategy: type-guard
Validate before calling
static boolean isDeserializableEntry(Map.Entry<?, ?> e) {
return e.getKey() instanceof Number || e.getKey() instanceof byte[];
} Type guard
static boolean canAddKey(Object key) {
return key instanceof Number || key instanceof byte[];
} Try / catch
try {
SpectatorHistogram h = SpectatorHistogram.deserialize(map);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Cannot add")) {
throw new IAE("Serialized map has non-numeric keys; regenerate with SpectatorHistogram.serialize", e);
}
throw e;
} Prevention
- Ensure serialization preserves numeric map key types (avoid generic JSON mappers that stringify keys).
- Guard each map key with instanceof Number before deserializing.
- Round-trip a sample histogram in tests to catch serde drift.
When it happens
Trigger: deserialize() iterating a HashMap whose keys are e.g. Strings, Integers-as-Strings, or other objects rather than Number/byte[] entries; passing a map from a foreign serializer into SpectatorHistogram.deserialize.
Common situations: JSON round-tripping turns integer map keys into Strings, so deserializing JSON that was not produced by SpectatorHistogram's own serde hits this; custom aggregation plugins feed wrongly typed maps.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Cannot deserialize type[%s] to an RoaringBitmap64Counter:
- Object cannot be deserialized to a Spectator Histogram
- Expected a Number, but received [%s] of type [%s]
- Object is not of a type[%s] that can be deserialized to sket
- Object is not of a type that can be deserialized to a quanti
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/05d6ff67fdabb951.
Report an issue: GitHub.