oracle/graal · error · IllegalArgumentException

Annotation value type

Error message

Annotation value type %s has no host class

What it means

HostAnnotationValueConverter.toAnnotation resolves the annotation's ResolvedJavaType to a host Class via the caller-supplied typeToClass function; if that function returns null for the annotation type itself, this IllegalArgumentException is thrown naming the type. It means the type lookup/mapping you provided cannot find a host class for an annotation type that actually has elements to convert.

Solutions

  1. Extend the typeToClass function to resolve the named annotation type (delegate to Class.forName with the right loader, or a JVMCI host-mapping service).
  2. Ensure the classloader used for lookup can see the annotation interface (add the containing module/jar).
  3. If the type genuinely has no host class, filter such AnnotationValues out before conversion instead of calling toAnnotation.
  4. Check annotationValue.isError() first — error values carry their own exception path.

Example fix

// before
Function<ResolvedJavaType, Class<?>> f = t -> JAVA_ONLY.get(t.toJavaName()); // null for com.foo.Bar
// after
Function<ResolvedJavaType, Class<?>> f = t -> Class.forName(t.toJavaName(), false, appLoader);
Defensive patterns

Strategy: validation

Validate before calling

ResolvedJavaType at = annotationValue.getAnnotationType();
if (typeToClass.apply(at) == null) { /* exclude or resolve the type before calling toAnnotation */ }

Try / catch

try { HostAnnotationValueConverter.toAnnotation(av, Expected.class, fn); } catch (IllegalArgumentException e) { if (e.getMessage().contains("no host class")) { /* widen fn's loader/search set */ } else throw e; }

Prevention

When it happens

Trigger: Calling toAnnotation with a typeToClass implementation that returns null for the annotation's own type — e.g. a lookup restricted to a specific classloader that cannot see the annotation, or that only maps a whitelist of types.

Common situations: Custom classloader hierarchies (embedded/isolated loads), snapshot-replay where the annotation type was not recorded, or a converter configured to map only java.* types while the annotation lives in a jdk.internal.* or application package.

Related errors


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

Appendix: source

Thrown at compiler/src/jdk.graal.compiler.vmaccess/src/jdk/graal/compiler/vmaccess/HostAnnotationValueConverter.java:92

     * @param annotationValue the annotation metadata to convert
     * @param expectedType the annotation type to which the result must be assignable
     * @param typeToClass maps JVMCI types to their corresponding host classes
     * @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();

View on GitHub (pinned to a66e9ccd1d)