oracle/graal · error · IllegalArgumentException
Annotation value type
Error message
Annotation value type %s is not an annotation interface
What it means
After resolving the annotation value's type to a host Class, toAnnotation verifies it is actually an annotation interface via Class.isAnnotation(); if not (a class, interface, or enum was found where an annotation type was expected), this exception names the offending class. It guards the subsequent asSubclass(Annotation.class) from failing less clearly.
Solutions
- Fix the type resolution so the correct annotation interface is returned (fully qualified binary names, correct loader).
- Eliminate the name collision: rename the shadowing class or adjust loader delegation.
- Add a pre-check Class.isAnnotation() in your own lookup with a descriptive error before calling toAnnotation.
- Verify no stale compiled classes are on the classpath overriding the expected annotation.
Example fix
// before
Class<?> c = appLoader.loadClass("com.foo.MyAnno" /* actually a plain class */);
// after
Class<?> c = appLoader.loadClass("com.foo.MyAnno");
if (!c.isAnnotation()) throw new IllegalStateException(c + " shadows annotation com.foo.MyAnno"); Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = typeToClass.apply(annotationValue.getAnnotationType());
if (c != null && !c.isAnnotation()) { /* wrong type resolved — fix mapping before converting */ } Type guard
static boolean isAnnotationClass(Class<?> c) { return c != null && c.isAnnotation(); } Try / catch
try { toAnnotation(av, Expected.class, fn); } catch (IllegalArgumentException e) { if (e.getMessage().contains("not an annotation interface")) { /* binary-name collision: audit classloaders */ } else throw e; } Prevention
- Resolve types by binary name with an explicit loader.
- Avoid simple-name-based type mappings.
- Watch for application classes shadowing annotation binary names.
When it happens
Trigger: typeToClass returns a non-annotation Class for the AnnotationValue's type — typically because two different types share the binary name across classloaders, or a lookup function maps by simple name and picks the wrong type.
Common situations: Classloader shadowing where an application class shadows an annotation's binary name; typo'd manual type mappings; replay/snapshot systems that recorded a placeholder class for what should be an annotation.
Related errors
- Annotation class element type
- Annotation value type
- Annotation member . does not match declared type
- Annotation value type
- Unsupported annotation error element
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/3b7e034f57b11461.
Report an issue: GitHub.
Appendix: source
Thrown at compiler/src/jdk.graal.compiler.vmaccess/src/jdk/graal/compiler/vmaccess/HostAnnotationValueConverter.java:95
* @return the converted annotation, or {@code null} when {@code annotationValue} is null
* @throws IllegalArgumentException if the annotation or one of its elements is incompatible
* with its declared type
*/
public static <T extends Annotation> T toAnnotation(AnnotationValue annotationValue, Class<T> expectedType, Function<ResolvedJavaType, Class<?>> typeToClass) {
if (annotationValue == null) {
return null;
}
Objects.requireNonNull(expectedType);
Objects.requireNonNull(typeToClass);
if (annotationValue.isError()) {
throw annotationValue.getError();
}
Class<?> actualType = typeToClass.apply(annotationValue.getAnnotationType());
if (actualType == null) {
throw new IllegalArgumentException("Annotation value type " + annotationValue.getAnnotationType().toJavaName() + " has no host class");
}
if (!actualType.isAnnotation()) {
throw new IllegalArgumentException("Annotation value type " + actualType.getName() + " is not an annotation interface");
}
if (!expectedType.isAssignableFrom(actualType)) {
throw new IllegalArgumentException("Annotation value type " + actualType.getName() + " is not assignable to " + expectedType.getName());
}
Class<? extends Annotation> annotationType = actualType.asSubclass(Annotation.class);
Annotation annotation = annotationValue.toAnnotation(annotationType, (value, type) -> createAnnotation(value, type, typeToClass));
return expectedType.cast(annotation);
}
/**
* Materializes a host-owned JDK annotation proxy from JVMCI annotation metadata.
*/
private static <T extends Annotation> T createAnnotation(AnnotationValue annotationValue, Class<T> annotationType, Function<ResolvedJavaType, Class<?>> typeToClass) {
Map<String, Object> memberValues = new LinkedHashMap<>();
Map<String, Object> annotationElements = annotationValue.getElements();
AnnotationValueType annotationValueType = AnnotationValueType.getInstance(annotationValue.getAnnotationType());
AnnotationValueValidation.validateElements(annotationValue, annotationValueType);
Map<String, Object> memberDefaults = annotationValueType.memberDefaults();View on GitHub (pinned to a66e9ccd1d)