oracle/graal · error · IllegalArgumentException
Bad argument kind at index {}: expected Byte, got {}
Error message
Bad argument kind at index {}: expected Byte, got {} What it means
Thrown by the argument marshalling switch in EspressoExternalResolvedJavaMethod.invoke when a parameter is declared byte but the argument constant's kind is not Byte. Byte is a source kind in JVMCI (not a stack kind), so constants easily lose it: most JVMCI utilities store bytes as Int constants. The adapter's switch is deliberately conservative for subword target types — only Int, Char, Short, and Float/Double parameters accept wider input sets; a Byte parameter demands a Byte constant.
Source
Thrown at espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaMethod.java:328
outputArgumentOffset = 1;
}
for (int i = 0; i < parameterCount; i++) {
JavaConstant argument = arguments[i];
JavaKind argumentKind = argument.getJavaKind();
/*
* Perform widening primitive conversions (JLS 5.1.2) in order to implement strict
* method invocation conversions (JLS 5.3). Also promote to stack kind.
*/
args[i + outputArgumentOffset] = switch (signature.getParameterKind(i)) {
case Boolean -> switch (argumentKind) {
case Boolean -> argument.asBoolean() ? 1 : 0;
default ->
throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Boolean, got " + argumentKind);
};
case Byte -> switch (argumentKind) {
case Byte -> argument.asInt();
default ->
throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Byte, got " + argumentKind);
};
case Char -> switch (argumentKind) {
case Char -> argument.asInt();
default ->
throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Char, got " + argumentKind);
};
case Short -> switch (argumentKind) {
case Short, Byte -> argument.asInt();
default ->
throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Short, got " + argumentKind);
};
case Int -> switch (argumentKind) {
case Int, Char, Short, Byte -> argument.asInt();
default ->
throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Int, got " + argumentKind);
};
case Long -> switch (argumentKind) {
case Long, Int, Char, Short, Byte -> argument.asLong();View on GitHub (pinned to a66e9ccd1d)
Solutions
- Rebox to the parameter kind with JavaConstant.forByte((byte) c.asInt()) before invoke
- Drive conversion from signature.getParameterKind(i) so each subword kind is restored after stack promotion
- Avoid generic 'normalize to Int' helpers on argument arrays destined for this API
Example fix
// before args[i] = JavaConstant.forInt(b); // parameter is byte // after args[i] = JavaConstant.forByte((byte) b);
Defensive patterns
Strategy: validation
Validate before calling
if (signature.getParameterKind(i) == JavaKind.Byte && args[i].getJavaKind() != JavaKind.Byte) {
args[i] = JavaConstant.forByte((byte) args[i].asInt());
} Prevention
- Byte parameters require Byte constants — the only subword accepted is none (unlike Short which takes Byte)
- Restore source kinds after any stack-kind normalization pass
- Store replay data with kind tags, not just values
When it happens
Trigger: Passing JavaConstant.forInt(0x7F) or a constant that came off the JVMCI stack (where byte is promoted to int) to a byte parameter at the reported index.
Common situations: Replaying bytecode-level values through the adapter; pipelines that rebox constants via asInt(); interop from languages without a distinct byte type.
Related errors
- Bad argument kind at index {}: expected Char, got {}
- Bad argument kind at index {}: expected Boolean, got {}
- Bad argument kind at index {}: expected Short, got {}
- Bad argument kind at index {}: expected Int, got {}
- Bad argument kind at index {}: expected Long, got {}
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/8eeeb8c4d35ddc35.
Report an issue: GitHub.