oracle/graal · error · IllegalArgumentException
undecoded bytes
Error message
undecoded bytes
What it means
IllegalArgumentException from OptionsEncoder.decode when the input byte array decodes its declared count of key/value pairs but bytes still remain in the stream (in.available() != 0). This means the payload is not a self-consistent OptionsEncoder payload: it was truncated/corrupted, produced by a different writer, or is raw data that merely looks like encoded options.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/OptionsEncoder.java:81
}
}
/**
* 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))) {
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final String key = in.readUTF();
final Object value = in.readTypedValue();
res.put(key, value);
}
if (in.available() != 0) {
throw new IllegalArgumentException(in.available() + " undecoded bytes");
}
} catch (IOException ioe) {
throw new IllegalArgumentException(ioe);
}
return res;
}
}
View on GitHub (pinned to a66e9ccd1d)
Solutions
- Verify provenance: re-encode the options on the same GraalVM build and compare lengths — a length mismatch confirms corruption or version skew.
- If the payload came from storage/network, re-obtain or re-transfer it (it is not recoverable client-side).
- Ensure encode and decode run on the same compiler version / same TypedDataInputStream tag set.
- Wrap decode at the trust boundary and reject malformed payloads explicitly rather than letting the exception propagate mid-initialization.
Example fix
// before
Map<String,Object> opts = OptionsEncoder.decode(readBytesFromFile(f));
// after: validate payload integrity before decoding
byte[] b = readBytesFromFile(f);
if (b.length < 4) throw new IllegalArgumentException("payload too short");
Map<String,Object> opts = OptionsEncoder.decode(b); Defensive patterns
Strategy: validation
Validate before calling
// Cheap integrity check before decode: first 4 bytes declare entry count;
// verify payload length is at least a minimal encoded body
static boolean looksLikeEncodedOptions(byte[] b) {
if (b == null || b.length < 6) return false;
int count = ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF);
return count >= 0 && count * 4 <= b.length; // each entry needs tag+len at minimum
} Try / catch
try {
return OptionsEncoder.decode(payload);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().endsWith("undecoded bytes")) {
logCorruptPayload(payloadId); // re-fetch / discard artifact
return Map.of();
}
throw e;
} Prevention
- Store a checksum (CRC32/SHA-256) alongside encoded option payloads and verify before decoding.
- Write payloads atomically (temp file + rename) so partial writes never look like valid payloads.
- Pin encoder/decoder to the same compiler version in mixed-version deployments.
When it happens
Trigger: Calling OptionsEncoder.decode(input) where input was truncated, padded, concatenated with other data, or produced by a mismatched encoder (different type-tag scheme or version) — readInt consumed the size, the loop consumed size entries, and trailing bytes are left over.
Common situations: Reading encoded options embedded in a compiled artifact after a partial write or file corruption; version skew where the encoder wrote extra fields the decoder loop does not consume; feeding an arbitrary byte[] (e.g. from a different format) into decode by mistake.
Related errors
- Key: %s, Value: %s, Value type: %s
- 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/e7e4f91b8444bbe8.
Report an issue: GitHub.