{"record":{"id":"3b7e034f57b11461","repo":"oracle/graal","slug":"annotation-value-type-s-is-not-an-annotation-inte","errorCode":null,"errorMessage":"Annotation value type %s is not an annotation interface","messagePattern":"Annotation value type (.+?) is not an annotation interface","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler.vmaccess/src/jdk/graal/compiler/vmaccess/HostAnnotationValueConverter.java","lineNumber":95,"sourceCode":"     * @return the converted annotation, or {@code null} when {@code annotationValue} is null\n     * @throws IllegalArgumentException if the annotation or one of its elements is incompatible\n     *             with its declared type\n     */\n    public static <T extends Annotation> T toAnnotation(AnnotationValue annotationValue, Class<T> expectedType, Function<ResolvedJavaType, Class<?>> typeToClass) {\n        if (annotationValue == null) {\n            return null;\n        }\n        Objects.requireNonNull(expectedType);\n        Objects.requireNonNull(typeToClass);\n        if (annotationValue.isError()) {\n            throw annotationValue.getError();\n        }\n        Class<?> actualType = typeToClass.apply(annotationValue.getAnnotationType());\n        if (actualType == null) {\n            throw new IllegalArgumentException(\"Annotation value type \" + annotationValue.getAnnotationType().toJavaName() + \" has no host class\");\n        }\n        if (!actualType.isAnnotation()) {\n            throw new IllegalArgumentException(\"Annotation value type \" + actualType.getName() + \" is not an annotation interface\");\n        }\n        if (!expectedType.isAssignableFrom(actualType)) {\n            throw new IllegalArgumentException(\"Annotation value type \" + actualType.getName() + \" is not assignable to \" + expectedType.getName());\n        }\n        Class<? extends Annotation> annotationType = actualType.asSubclass(Annotation.class);\n        Annotation annotation = annotationValue.toAnnotation(annotationType, (value, type) -> createAnnotation(value, type, typeToClass));\n        return expectedType.cast(annotation);\n    }\n\n    /**\n     * Materializes a host-owned JDK annotation proxy from JVMCI annotation metadata.\n     */\n    private static <T extends Annotation> T createAnnotation(AnnotationValue annotationValue, Class<T> annotationType, Function<ResolvedJavaType, Class<?>> typeToClass) {\n        Map<String, Object> memberValues = new LinkedHashMap<>();\n        Map<String, Object> annotationElements = annotationValue.getElements();\n        AnnotationValueType annotationValueType = AnnotationValueType.getInstance(annotationValue.getAnnotationType());\n        AnnotationValueValidation.validateElements(annotationValue, annotationValueType);\n        Map<String, Object> memberDefaults = annotationValueType.memberDefaults();","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler.vmaccess/src/jdk/graal/compiler/vmaccess/HostAnnotationValueConverter.java#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nClass<?> c = appLoader.loadClass(\"com.foo.MyAnno\" /* actually a plain class */);\n// after\nClass<?> c = appLoader.loadClass(\"com.foo.MyAnno\");\nif (!c.isAnnotation()) throw new IllegalStateException(c + \" shadows annotation com.foo.MyAnno\");","handlingStrategy":"validation","validationCode":"Class<?> c = typeToClass.apply(annotationValue.getAnnotationType());\nif (c != null && !c.isAnnotation()) { /* wrong type resolved — fix mapping before converting */ }","typeGuard":"static boolean isAnnotationClass(Class<?> c) { return c != null && c.isAnnotation(); }","tryCatchPattern":"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; }","preventionTips":["Resolve types by binary name with an explicit loader.","Avoid simple-name-based type mappings.","Watch for application classes shadowing annotation binary names."],"tags":["graalvm","jvmci","annotations","classloading"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}