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

  1. Confine every HSObject use to the JNIMethodScope it was created in; never leak it past close().
  2. If an object must outlive a scope, create a global reference (NewGlobalRef) and use that handle instead of the local wrapper.
  3. 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

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


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/7fbff8df8cc1f615. Report an issue: GitHub.