oracle/graal · error · IllegalArgumentException
Key: %s, Value: %s, Value type: %s
Error message
Key: %s, Value: %s, Value type: %s
What it means
IllegalArgumentException from OptionsEncoder.encode when one of the values in the options map cannot be serialized: the underlying TypedDataOutputStream.writeTypedValue rejects its type and encode rethrows with the offending key, value, and value class attached. OptionsEncoder is used to persist/transfer compiler option maps (e.g. encoded Truffle compiler options attached to compiled artifacts), so this means an option value of a non-encodable type entered the map.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/OptionsEncoder.java:55
private OptionsEncoder() {
}
/**
* Encodes {@code options} into a byte array.
*
* @throws IllegalArgumentException if any value in {@code options} is not supported
*/
public static byte[] encode(final Map<String, Object> options) {
try (ByteArrayOutputStream baout = new ByteArrayOutputStream()) {
try (TypedDataOutputStream out = new TypedDataOutputStream(baout)) {
out.writeInt(options.size());
for (Map.Entry<String, Object> e : options.entrySet()) {
out.writeUTF(e.getKey());
try {
out.writeTypedValue(e.getValue());
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException(String.format("Key: %s, Value: %s, Value type: %s",
e.getKey(), e.getValue(), e.getValue().getClass()), iae);
}
}
}
return baout.toByteArray();
} catch (IOException ioe) {
throw new IllegalArgumentException(ioe);
}
}
/**
* Decodes {@code input} into a name/value map.
*
* @throws IllegalArgumentException if {@code input} cannot be decoded
*/
public static Map<String, Object> decode(byte[] input) {
Map<String, Object> res = new LinkedHashMap<>();
try (TypedDataInputStream in = new TypedDataInputStream(new ByteArrayInputStream(input))) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Read the message: it names the exact Key, Value, and Value type — remove that entry or convert it to a supported type (String, Integer, Long, Float, Double, Boolean, Character, byte[], enum).
- If the value is a custom object, serialize it to a String (e.g. toString or JSON) before putting it in the options map.
- If it is a legitimately new option type, extend TypedDataOutputStream.writeTypedValue (and the matching reader) with a new type tag instead of working around the exception.
- Check for version skew: encode and decode must run on GraalVM builds with the same supported-type set.
Example fix
// before
Map<String,Object> opts = new HashMap<>();
opts.put("myUri", URI.create("http://x")); // not encodable
OptionsEncoder.encode(opts);
// after
opts.put("myUri", URI.create("http://x").toString()); // String is encodable
OptionsEncoder.encode(opts); Defensive patterns
Strategy: type-guard
Validate before calling
static final Set<Class<?>> SUPPORTED = Set.of(
Boolean.class, Character.class, Integer.class, Long.class,
Float.class, Double.class, String.class, byte[].class);
static boolean isEncodable(Object v) {
return v == null || SUPPORTED.contains(v.getClass()) || v.getClass().isEnum();
}
// before encode
options.forEach((k, v) -> { if (!isEncodable(v)) throw new IllegalArgumentException(k + " -> " + v.getClass()); }); Type guard
static boolean isEncodableOptionValue(Object v) {
if (v == null) return true;
Class<?> c = v.getClass();
return c == Boolean.class || c == Character.class || c == Integer.class
|| c == Long.class || c == Float.class || c == Double.class
|| c == String.class || c == byte[].class || c.isEnum();
} Try / catch
try {
byte[] enc = OptionsEncoder.encode(options);
} catch (IllegalArgumentException e) {
// message already names Key/Value/type — surface it to config diagnostics
throw new ConfigException("non-encodable option: " + e.getMessage(), e);
} Prevention
- Restrict option maps to String + boxed primitives + enums by convention.
- Add a round-trip unit test (encode->decode->equals) for every option set you persist.
- Keep encoder and decoder on the same GraalVM build to avoid type-tag skew.
When it happens
Trigger: Calling OptionsEncoder.encode(map) where map contains a value whose class is not one of the supported primitive/String/Enum/byte[] types handled by TypedDataOutputStream.writeTypedValue — for example a custom object, java.net.URI, or a boxed type added by a new option.
Common situations: Adding a new compiler option whose value type is not in the typed-stream whitelist and then encoding options for storage or IPC; mixing option maps across GraalVM versions where a value type changed; passing a decoded-then-mutated map back in with an extra debugging entry.
Related errors
- undecoded bytes
- Unsupported type: Value: %s, Value type: %s
- SpectrePHTBarriers can be set to 'AllTargets' if and only if
- register %s is not allocatable
- Unsupported type: Value: %s, Value type: %s
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/8c8ded74bac251cd.
Report an issue: GitHub.