oracle/graal · error · IllegalArgumentException
hashcode must be > 0
Error message
hashcode must be > 0
What it means
IllegalArgumentException from EspressoExternalConstantReflectionProvider.makeIdentityHashCode: the requested identity-hashcode value was <= 0. The JVM only ever assigns positive identity hashcodes, and the underlying VM helper (invokeJVMCIHelper("makeIdentityHashCode", ...)) requires a positive value, so the reflection provider validates before crossing into the VM. This method is the JVMCI bridge behind IdentityHashCodes.set during continuation deserialization.
Source
Thrown at espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantReflectionProvider.java:351
throw new IllegalArgumentException("Constant has unexpected type " + constant.getClass() + ": " + constant);
}
return objectConstant.guestHashCode();
}
@Override
public int makeIdentityHashCode(JavaConstant constant, int requestedValue) {
JavaKind kind = Objects.requireNonNull(constant).getJavaKind();
if (kind != JavaKind.Object) {
throw new IllegalArgumentException("Constant has unexpected kind " + kind + ": " + constant);
}
if (constant.isNull()) {
throw new NullPointerException();
}
if (!(constant instanceof EspressoExternalObjectConstant objectConstant)) {
throw new IllegalArgumentException("Constant has unexpected type " + constant.getClass() + ": " + constant);
}
if (requestedValue <= 0) {
throw new IllegalArgumentException("hashcode must be > 0");
}
return access.invokeJVMCIHelper("makeIdentityHashCode", objectConstant.getValue(), requestedValue).asInt();
}
@Override
public AbstractEspressoResolvedInstanceType getTypeForStaticBase(JavaConstant staticBase) {
if (!(staticBase instanceof EspressoExternalObjectConstant objectConstant)) {
return null;
}
Value type = access.invokeJVMCIHelper("getTypeForStaticBase", objectConstant.getValue());
if (type.isNull()) {
return null;
}
return new EspressoExternalResolvedInstanceType(access, type);
}
}
View on GitHub (pinned to a66e9ccd1d)
Solutions
- Pass a strictly positive int; validate requestedValue > 0 at the call site before invoking the provider.
- Fix the persistence encoding so recorded hashcodes round-trip as unsigned/positive 31-bit ints.
- If no hashcode was recorded for the object, skip the call entirely instead of passing 0.
- Check for integer sign-extension bugs when the recorded value traveled through smaller integer types.
Example fix
// before
provider.makeIdentityHashCode(constant, (short) recordedHash); // sign-extended, can be <= 0
// after
if (recordedHash > 0) {
provider.makeIdentityHashCode(constant, recordedHash);
} // else: no recorded hashcode, let the VM assign one Defensive patterns
Strategy: validation
Validate before calling
if (requestedValue <= 0) {
throw new IllegalArgumentException("requestedValue must be a positive int, got " + requestedValue);
}
provider.makeIdentityHashCode(constant, requestedValue); Prevention
- Validate requestedValue > 0 before calling makeIdentityHashCode.
- Skip the call when no hashcode was recorded instead of passing 0.
- Encode persisted hashcodes so they round-trip as positive 31-bit ints (no sign truncation).
When it happens
Trigger: Calling makeIdentityHashCode(constant, 0), with a negative value, or with a recorded hashcode that was decoded/sign-truncated to <= 0 (e.g. stored in a signed byte/short field somewhere in the persistence layer).
Common situations: Identity hashcodes persisted through a channel that cannot represent the full positive int range (ProtoBuf zigzag mishandling, JSON as signed int16); defaulting the 'requested value' parameter to 0 when no recording exists.
Related errors
- Out of scratch registers: %s
- Annotation member %s.%s does not match declared type %s
- Expected a non-void primitive kind, got {}
- Negative array size: {}
- Expected an array constant for src, got
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/f6e0293d2c9719bb.
Report an issue: GitHub.