oracle/graal · error · AssertionError

Both 'value' and 'type' are specified for @EspressoSubstitut

Error message

Both 'value' and 'type' are specified for @EspressoSubstitution {className}

What it means

AssertionError from the EspressoSubstitution annotation processor at compile time: a single @EspressoSubstitution annotation specified both the class member ('value') and the 'type' string. The processor counts successful target-resolution schemes and refuses ambiguity when more than one is set.

Source

Thrown at espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionProcessor.java:195

            targetClassName = "L" + className.substring("Target_".length()).replace("_", "/") + ";";
        }
        int successfulScheme = 0;
        // If it exists, collect the value of EspressoSubstitutions.value()
        TypeMirror targetClass = getAnnotationValue(annotation, "value", TypeMirror.class);
        assert targetClass != null; // Default value is EspressoSubstitutions.class
        // If it exists, collect the value of EspressoSubstitutions.type()
        String targetType = getAnnotationValue(annotation, "type", String.class);

        if (!processingEnv.getTypeUtils().isSameType(targetClass, espressoSubstitutions.asType())) {
            targetClassName = "L" + targetClass.toString().replace(".", "/") + ";";
            successfulScheme++;
        }
        if (targetType != null && !targetType.isEmpty()) {
            targetClassName = targetType;
            successfulScheme++;
        }
        if (successfulScheme > 1) {
            throw new AssertionError("Both 'value' and 'type' are specified for @EspressoSubstitution " + className);
        }

        // Get the name provider. Will override the previously obtained target class name.
        TypeMirror defaultNameProvider = getNameProvider(annotation);

        // Thr group to be used for the @Collect annotation
        TypeMirror group = getAnnotationValue(annotation, "group", TypeMirror.class);

        for (Element element : substitution.getEnclosedElements()) {
            processSubstitution(element, className, defaultNameProvider, targetClassName, typeElement, group);
        }
    }

    private void checkParameterOrReturnType(String headerMessage, TypeMirror typeMirror, Element element, boolean allowPointerType) {
        if (typeMirror.getKind().isPrimitive()) {
            if (getAnnotation(typeMirror, javaType) != null) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                                headerMessage + " (primitive type) cannot be annotated with @JavaType", element);

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Remove one of the two attributes: keep 'value' when the target class is visible at compile time, otherwise keep only 'type'.
  2. If the class is not on the processor classpath, use type = "Lcom/example/MyClass;" exclusively.
  3. Rebuild so the annotation processor re-runs and confirms the error is gone.

Example fix

// before
@EspressoSubstitution(value = ArrayList.class, type = "java/util/ArrayList")
static class MySubstitution { ... }

// after
@EspressoSubstitution(value = ArrayList.class)
static class MySubstitution { ... }
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Writing @EspressoSubstitution(value = MyClass.class, type = "com/example/MyClass") on the same substitution; adding a 'type' attribute to an existing annotation that already names a class; copy-pasting a template that fills in both attributes.

Common situations: Annotating a class whose target is not on the annotation-processor classpath, then also adding type= as a workaround; upgrading Espresso versions where templates/examples changed; IDE auto-completing both attributes.

Related errors


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