oracle/graal · error · IllegalArgumentException
%s is not an annotation interface
Error message
%s is not an annotation interface
What it means
checkNonExistingGlobalReference scans all registered Cleaner entries and throws IllegalArgumentException if an identical object already has a global JNI reference (checked via JNI IsSameObject). The library intentionally avoids duplicate global refs for the same host object, because each global ref is a leak-prone native resource and the cleaner registry assumes one entry per object.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueSupport.java:67
/**
* Support for parsing class file annotation attributes.
*/
public class AnnotationValueSupport {
/**
* Gets the annotation directly present on {@code annotated} whose type is
* {@code annotationType}. Class initialization is not triggered for enum types referenced by
* the returned annotation. This method ignores inherited annotations.
*
* @param annotationType the type object corresponding to the annotation interface type
* @return {@code annotated}'s annotation for the specified annotation type if directly present
* on this element, else null
* @throws IllegalArgumentException if {@code annotationType} is not an annotation interface
* type
*/
public static AnnotationValue getDeclaredAnnotationValue(ResolvedJavaType annotationType, Annotated annotated) {
if (!annotationType.isAnnotation()) {
throw new IllegalArgumentException(annotationType.toJavaName() + " is not an annotation interface");
}
return getDeclaredAnnotationValues(annotated).get(annotationType);
}
/**
* Checks if an annotation of the specified type is directly present on the given
* {@code annotated} element. Class initialization is not triggered for enum types referenced by
* the returned annotation. This method ignores inherited annotations.
*
* @param annotationType the type object corresponding to the annotation interface type
* @return true if an annotation of the specified type is directly present on the given
* {@code annotated} element
* @throws IllegalArgumentException if {@code annotationType} is not an annotation interface
* type
*/
public static boolean isAnnotationPresent(ResolvedJavaType annotationType, Annotated annotated) {
return getDeclaredAnnotationValue(annotationType, annotated) != null;
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Track existing global refs and reuse them instead of creating a second one for the same object.
- Delete the old global reference (JNIUtil.release / DeleteGlobalRef) before creating a new one for the same object.
- Guard creation code with a single initialization point (e.g. synchronized lazy init or a static registry keyed by object) so the same object is never registered twice.
Example fix
// before JObject g1 = NewGlobalRef(env, obj, "obj"); JObject g2 = NewGlobalRef(env, obj, "obj"); // throws // after JObject g = registry.getOrCreateGlobalRef(env, obj); // returns existing ref if IsSameObject match // and call JNIUtil.release(env, g) when done, before re-creating
Defensive patterns
Strategy: validation
Validate before calling
// Before creating a global ref, check whether you already hold one for the same object:
JObject existing = registry.lookupSameObject(env, candidate);
if (existing != null && JNIUtil.IsSameObject(env, existing, candidate)) {
return existing; // reuse, do not create a duplicate
}
NewGlobalRef(env, candidate, "name"); Try / catch
try {
NewGlobalRef(env, obj, tag);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Global JNI handle already exists")) {
// fetch the existing ref from your registry instead of creating a new one
} else {
throw e;
}
} Prevention
- Centralize global-reference creation in one registry so the same object is pinned once.
- Always pair NewGlobalRef with release/DeleteGlobalRef before re-pinning the same object.
- Make init code idempotent (guarded by a 'initialized' flag) so retries don't double-register.
When it happens
Trigger: Calling NewGlobalRef (the JNIUtil helper that routes through this check) twice for the same host object without deleting the first reference; retry paths that re-create a global ref after a failure without cleaning up; two features both pinning the same well-known object (e.g. the same class or exception instance).
Common situations: Idempotency bugs in JNI glue code where a setup routine runs twice; concurrent initialization racing to pin the same singleton object; missing DeleteGlobalRef on a previous reference before re-creating it.
Related errors
- expected %s, got %s
- Cannot look up %s annotation at Native Image runtime
- %s has params
- %s is not an enum type
- Could not parse bytes for type annotations
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/b296455df64d4ed3.
Report an issue: GitHub.