oracle/graal · error · IllegalArgumentException
expected %s, got %s
Error message
expected %s, got %s
What it means
HSObject wraps a JNI object handle and is managed through an intrusive free list: when the handle is reclaimed, its 'next' pointer is set to point at itself, marking it invalid. getHandle() throws IllegalArgumentException if it detects that self-loop, i.e. the JNI reference behind this HSObject has already been freed/reclaimed and must not be used again. Using the wrapper after reclamation is a native-memory use-after-free analogue.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueParser.java:308
return error != null ? error : List.of(result);
}
private static String getUtf8At(ConstantPool cp, int cpi) {
try {
return cp.lookupUtf8(cpi);
} catch (IndexOutOfBoundsException e) {
// Translate to IllegalArgumentException to match
// jdk.internal.reflect.ConstantPool
throw new IllegalArgumentException(e);
}
}
private static Object getPrimitiveConstAt(JavaKind kind, ConstantPool cp, int cpi) {
try {
PrimitiveConstant o = (PrimitiveConstant) cp.lookupConstant(cpi);
JavaKind stackKind = kind.getStackKind();
if (o.getJavaKind() != stackKind) {
throw new IllegalArgumentException("expected " + stackKind + ", got " + o.getJavaKind());
}
return JavaConstant.forPrimitive(kind, o.getRawValue()).asBoxedPrimitive();
} catch (ClassCastException | IndexOutOfBoundsException e) {
// Translate to IllegalArgumentException to match
// jdk.internal.reflect.ConstantPool
throw new IllegalArgumentException(e);
}
}
private static Object parseArrayElements(int length,
ByteBuffer buf,
int expectedTag,
Supplier<Object> parseElement) {
Object[] result = new Object[length];
Object invalidTag = null;
for (int i = 0; i < result.length; i++) {
int tag = buf.get();
if (tag == expectedTag) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Confine every HSObject use to the JNIMethodScope it was created in; never leak it past close().
- If an object must outlive a scope, create a global reference (NewGlobalRef) and use that handle instead of the local wrapper.
- Audit exception handlers that close scopes and make sure they null out cached HSObjects afterwards.
Example fix
// before
try (JNIMethodScope scope = new JNIMethodScope("op", env)) {
obj = someCallReturningHSObject(env);
}
useHandle(obj.getHandle()); // scope already closed -> reclaimed
// after
try (JNIMethodScope scope = new JNIMethodScope("op", env)) {
handle = JNIUtil.newGlobalRef(env, someCallReturningHSObject(env), "obj");
}
useHandle(handle); // global ref survives the scope Defensive patterns
Strategy: validation
Validate before calling
// HSObject marks reclaimed handles by pointing next at itself:
static boolean isReclaimed(HSObject o) {
return o != null && o.next == o; // conceptually; in practice just never use HSObject past scope close()
} Try / catch
try {
handle = obj.getHandle();
} catch (IllegalArgumentException e) {
// wrapper was invalidated with its scope; treat as stale-reference bug, do not retry
throw new IllegalStateException("HSObject used outside its JNIMethodScope", e);
} Prevention
- Never let an HSObject escape the JNIMethodScope that produced it.
- Promote objects that must survive a scope with NewGlobalRef and pass the global handle around instead.
- Null out cached wrappers in finally blocks that close scopes.
When it happens
Trigger: Calling getHandle() on an HSObject after HSObject.invalidate(...) reclaimed it (as done in JNIMethodScope.close()); keeping an HSObject obtained inside one JNIMethodScope and using it after that scope closed; holding an HSObject across JNI local-frame pops after cleanHandles() drained the cleaners queue.
Common situations: Caching JNI object wrappers beyond the lifetime of their scope/frame; exception paths that close a scope and then continue using objects created inside it; long-lived Espresso/JNI feature code reusing stale wrappers.
Related errors
- Unexpected enum type: %s
- %s is not an annotation interface
- Cannot look up %s annotation at Native Image runtime
- %s has params
- %s is not an enum type
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/7fbff8df8cc1f615.
Report an issue: GitHub.