oracle/graal · error · IllegalArgumentException
Unexpected value: ${instance}
Error message
Unexpected value: ${instance} What it means
The primitive-constant serializer accepts only Integer, Byte, Short, Long, Float, and Double and encodes them as hex strings. Serialize() throws IllegalArgumentException('Unexpected value: ...') when it receives any other wrapper type — typically Boolean, Character, or a boxed value from a different serializer version.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/JsonReplayCodec.java:456
return Number.class;
}
@Override
public String tag() {
return "number";
}
@Override
public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder, RecursiveSerializer serializer) throws IOException {
objectBuilder.append("class", instance.getClass().getSimpleName());
String value = switch (instance) {
case Integer n -> Integer.toHexString(n);
case Byte n -> Integer.toHexString(n);
case Short n -> Integer.toHexString(n);
case Long n -> Long.toHexString(n);
case Float n -> floatToHex(n);
case Double n -> doubleToHex(n);
default -> throw new IllegalArgumentException("Unexpected value: " + instance);
};
objectBuilder.append("value", value);
}
@Override
public Object deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, CompilationProxies.ProxyFactory proxyFactory) {
String className = (String) json.get("class");
String value = (String) json.get("value");
switch (className) {
case "Integer" -> {
return Integer.parseUnsignedInt(value, HEX_RADIX);
}
case "Byte" -> {
return (byte) Integer.parseUnsignedInt(value, HEX_RADIX);
}
case "Short" -> {
return (short) Integer.parseUnsignedInt(value, HEX_RADIX);
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Add Boolean/Character cases to the switch in JsonReplayCodec.java:456 (serialize 'true'/'false' or the char) and matching deserialize cases
- Check the chained value in the message to confirm exactly which type leaked through, then trace which serializer should have claimed it
- Update both halves of the codec (serialize AND deserialize) so the file stays round-trippable
Example fix
// before
String value = switch (instance) {
case Integer n -> Integer.toHexString(n);
// ...
default -> throw new IllegalArgumentException("Unexpected value: " + instance);
};
// after: add
case Boolean b -> Boolean.toString(b);
case Character c -> Integer.toHexString(c);
// and the mirror cases in deserialize(...) Defensive patterns
Strategy: type-guard
Type guard
static boolean isSupportedPrimitiveConstant(Object v) {
return v instanceof Integer || v instanceof Byte || v instanceof Short
|| v instanceof Long || v instanceof Float || v instanceof Double;
} Prevention
- When introducing a new parameter type into the recorded compiler interface, extend the constant serializer switch in the same change
- Add a round-trip unit test (serialize then deserialize) for every supported primitive wrapper
When it happens
Trigger: Calling the JSON replay serializer on a recorded constant that is a Boolean or Character (or Void/other boxed primitive) — the pattern match in the switch falls through to default.
Common situations: A recorded compiler-interface method takes a boolean or char parameter and no dedicated serializer covers it; GraalVM version drift that starts recording a previously unsupported parameter type; custom compiler-interface additions.
Related errors
- Invalid replay string table index
- Invalid replay string definition index
- Invalid replay symbolic method index
- Invalid replay symbolic method definition index
- Invalid replay registration index
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/888e626d3c052ea3.
Report an issue: GitHub.