oracle/graal · error · IllegalArgumentException
Expected an espresso object constant, got {}
Error message
Expected an espresso object constant, got {} What it means
Thrown by EspressoExternalResolvedJavaField.writeValue when the field's JavaKind is Object but the value JavaConstant is neither null nor an EspressoExternalObjectConstant. For reference-typed fields the adapter can only forward a guest polyglot Value; the null constant is handled explicitly (boxed to null), so any other surviving constant class means the value originated outside the Espresso vmaccess layer and cannot be written into the guest object field. The message reports the actual constant class to identify the producer.
Source
Thrown at espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaField.java:145
}
if (receiver.isNull()) {
throw new IllegalArgumentException("Receiver is null");
}
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) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Create the value constant with the Espresso access layer so it is an EspressoExternalObjectConstant, or JavaConstant.NULL_POINTER when storing null
- For host objects, convert to a guest Value (context.asValue(...)/polyglot embedding) and wrap via the Espresso constant reflection before the write
- Audit caches keyed on JavaConstant for cross-backend contamination indicated by the class name in the message
Example fix
// before
field.writeValue(receiver, JavaConstant.forObject(hostString)); // foreign constant
// after
Value guestString = espressoAccess.invokeJVMCIHelper("toGuestString", hostString);
JavaConstant valueConst = espressoAccess.getConstantReflection().forObject(guestString);
field.writeValue(receiver, valueConst); Defensive patterns
Strategy: validation
Validate before calling
JavaKind fieldKind = field.getJavaKind();
boolean ok = fieldKind != JavaKind.Object
? value.getJavaKind() == fieldKind
: value.isNull() || value instanceof EspressoExternalObjectConstant;
if (!ok) throw new IllegalStateException("value constant not usable for field " + field.getName()); Try / catch
catch (IllegalArgumentException e) { if (e.getMessage() != null && e.getMessage().startsWith("Expected an espresso object constant")) { /* re-obtain constant from Espresso reflection */ } else throw e; } Prevention
- Never pass JavaConstant.forObject(hostObject) into guest field writes; convert the object to a guest Value first
- Use JavaConstant.NULL_POINTER (isNull path) rather than foreign null wrappers when storing null
- Centralize constant creation in one factory that talks only to the Espresso access layer
When it happens
Trigger: Writing a reference field with a constant from another JVMCI backend (host ConstantReflectionProvider box methods, Vivify/HotSpot object constants, or a custom JavaConstant). Writing a boxed-primitive constant into an Object field is also rejected because EspressoExternalObjectConstant.asBoxedPrimitive itself throws.
Common situations: Compiler passes that reuse host constants for guest stores; test harnesses using JavaConstant.forObject(hostInstance); pipelines where the ConstantReflectionProvider is swapped per-context but cached constants leak across contexts.
Related errors
- Expected an espresso object receiver, got {}
- Cannot bind label to negative position %d
- Expected value kind {} but got {}
- Bad receiver: expected Object, got {}
- Bad argument kind at index {}: expected Object, got {} wrapp
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/7c9509a4c0a671a9.
Report an issue: GitHub.