oracle/graal · error · IllegalArgumentException
Expected value kind {} but got {}
Error message
Expected value kind {} but got {} What it means
Thrown by EspressoExternalResolvedJavaField.writeValue when the field is primitive but the JavaKind of the value constant does not equal the field's kind. Unlike method invocation (which performs JLS widening conversions), field stores in this adapter are strict: an int field requires an Int-kind constant, a long field a Long-kind constant, etc. This mirrors Java store semantics (JLS 5.1.3 assignment conversion allows only identity or widening for primitives, but the adapter implements the exact-kind case and rejects everything else).
Source
Thrown at espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaField.java:149
if (!(receiver instanceof EspressoExternalObjectConstant espressoReceiver)) {
throw new IllegalArgumentException("Expected an espresso object receiver, got " + receiver.getClass().getName());
}
receiverValue = espressoReceiver.getValue();
}
JavaKind kind = getJavaKind();
final Object boxed;
if (kind == JavaKind.Object) {
if (value.isNull()) {
boxed = null;
} else if (value instanceof EspressoExternalObjectConstant objConst) {
boxed = objConst.getValue();
} else {
throw new IllegalArgumentException("Expected an espresso object constant, got " + value.getClass().getName());
}
} else {
if (kind != value.getJavaKind()) {
throw new IllegalArgumentException("Expected value kind " + kind + " but got " + value.getJavaKind());
}
boxed = value.asBoxedPrimitive();
}
getAccess().invokeJVMCIHelper("writeField", vmFieldMirror, receiverValue, boxed);
}
/**
* Gets a guest {@link java.lang.reflect.Field} value associated with this field, creating it
* first if necessary.
*/
Value getReflectFieldMirror() {
Value value = reflectFieldMirror;
if (value == null) {
value = getAccess().invokeJVMCIHelper("getReflectField", vmFieldMirror);
reflectFieldMirror = value;
}
return value;View on GitHub (pinned to a66e9ccd1d)
Solutions
- Convert the constant to the field's kind before writing: use JavaConstant conversion helpers or the field's getJavaKind() to drive forInt/forLong/forFloat/forDouble/forBoolean
- For integral widening, replicate the conversion yourself (e.g. JavaConstant.forLong(intConst.asInt()) for a long field)
- Log field.getName()+field.getJavaKind() next to value.getJavaKind() at the failure site to find the pass that mismatches kinds
Example fix
// before
field.writeValue(receiver, JavaConstant.forInt(size)); // field is long
// after
JavaKind fieldKind = field.getJavaKind();
JavaConstant converted = switch (fieldKind) {
case Long -> JavaConstant.forLong(size);
case Int -> JavaConstant.forInt(size);
default -> throw new AssertionError("unsupported " + fieldKind);
};
field.writeValue(receiver, converted); Defensive patterns
Strategy: validation
Validate before calling
JavaKind expected = field.getJavaKind();
JavaConstant v = switch (expected) {
case Long -> JavaConstant.forLong(raw.asLong());
case Int -> JavaConstant.forInt((int) raw.asLong());
case Double -> JavaConstant.forDouble(raw.asDouble());
case Float -> JavaConstant.forFloat((float) raw.asDouble());
default -> raw;
};
assert v.getJavaKind() == expected; Prevention
- Always drive constant reboxing from field.getJavaKind(), never from the value's current kind
- Remember JVMCI stack promotion turns byte/short/char/boolean into Int — restore the source kind before a store
- Field stores here are strict (no widening): convert explicitly like the method-invoke switch does
When it happens
Trigger: Writing JavaConstant.forInt(1) into a long field, a Float constant into a double field, or a widened/narrowed constant whose stack kind differs from the field kind after JVMCI kind promotion (e.g. an Int constant representing a boolean/short/char slot).
Common situations: Lowering passes that box/unbox or promote constants to stack kinds before storing; snapshots or replays where the field resolution changed between runs (field type changed in the guest class); generic 'set field by name' tooling that guesses the constant kind from a boxed Object instead of field.getJavaKind().
Related errors
- Expected an espresso object receiver, got {}
- Expected an espresso object constant, got {}
- Bad argument kind at index {}: expected Boolean, got {}
- Bad argument kind at index {}: expected Byte, got {}
- Bad argument kind at index {}: expected Char, got {}
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/2e0d6683b05b1787.
Report an issue: GitHub.