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
- Remove one of the two attributes: keep 'value' when the target class is visible at compile time, otherwise keep only 'type'.
- If the class is not on the processor classpath, use type = "Lcom/example/MyClass;" exclusively.
- 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
- Set exactly one of 'value' or 'type' per @EspressoSubstitution annotation.
- Use 'type' only when the target class is unavailable to the annotation processor.
- Fix annotation-processor errors immediately; they break the whole compilation.
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
- Unexpected tokens :${rule.substring(m.end(), m.regionEnd())}
- didn't consume all tokens
- Out of tokens
- Out of tokens looking for %s
- not enough inputs for ${descriptor.name}
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/ad19eeb2d796bee4.
Report an issue: GitHub.