oracle/graal · error · ElementException
Field cannot be both input and successor
Error message
Field cannot be both input and successor
What it means
Thrown by GraphNodeVerifier (part of the Graal node-info annotation processor) when a field in a Node class carries both @Input (or @OptionalInput) and @Successor annotations. Inputs and successors are distinct edge kinds in Graal's graph model with different invariants (inputs have usages, successors form the control-flow topology), so a single field cannot be both. It surfaces as a compile-time ElementException on the offending field.
Source
Thrown at compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java:123
private void scanFields(TypeElement node) {
TypeElement currentClazz = node;
do {
for (VariableElement field : ElementFilter.fieldsIn(currentClazz.getEnclosedElements())) {
Set<Modifier> modifiers = field.getModifiers();
if (modifiers.contains(STATIC) || modifiers.contains(TRANSIENT)) {
continue;
}
List<? extends AnnotationMirror> annotations = field.getAnnotationMirrors();
boolean isNonOptionalInput = findAnnotationMirror(annotations, Input) != null;
boolean isOptionalInput = findAnnotationMirror(annotations, OptionalInput) != null;
boolean isSuccessor = findAnnotationMirror(annotations, Successor) != null;
if (isNonOptionalInput || isOptionalInput) {
if (findAnnotationMirror(annotations, Successor) != null) {
throw new ElementException(field, "Field cannot be both input and successor");
} else if (isNonOptionalInput && isOptionalInput) {
throw new ElementException(field, "Inputs must be either optional or non-optional");
} else if (isAssignableWithErasure(field, NodeInputList)) {
if (modifiers.contains(FINAL)) {
throw new ElementException(field, "Input list field must not be final");
}
if (modifiers.contains(PUBLIC)) {
throw new ElementException(field, "Input list field must not be public");
}
} else {
if (!isAssignableWithErasure(field, Node) && field.getKind() == ElementKind.INTERFACE) {
throw new ElementException(field, "Input field type must be an interface or assignable to Node");
}
if (modifiers.contains(FINAL)) {
throw new ElementException(field, "Input field must not be final");
}
if (modifiers.contains(PUBLIC)) {
throw new ElementException(field, "Input field must not be public");View on GitHub (pinned to a66e9ccd1d)
Solutions
- Decide the edge's role: data/usage edge -> keep @Input/@OptionalInput; control-flow edge -> keep @Successor; remove the other annotation.
- If you truly need both relationships, model them as two separate fields.
- Re-run the build; the processor re-verifies on the next annotation-processing round.
Example fix
// before @Input @Successor Node next; // after (control-flow edge -> @Successor only) @Successor Node next;
Defensive patterns
Strategy: validation
Try / catch
This is a compile-time ElementException from the node-info processor; there is no runtime catch. CI must fail the build on it.
Prevention
- Annotate each Node field with exactly one edge kind: @Input, @OptionalInput, or @Successor.
- Model genuinely dual relationships as two fields.
- Review annotations explicitly whenever changing an edge's role in a node class.
When it happens
Trigger: Declaring a Node field annotated with e.g. @Input @Successor NodeClass field; or @OptionalInput @Successor. The verifier checks annotation mirrors directly on non-static, non-transient fields of classes it processes.
Common situations: Copy-paste when adding a successor field from an input field template; refactorings that change an edge's role without removing the old annotation; experimental node classes where the author was unsure which edge kind to use.
Related errors
- Inputs must be either optional or non-optional
- Unexpected tokens :${rule.substring(m.end(), m.regionEnd())}
- didn't consume all tokens
- Out of tokens
- Out of tokens looking for %s
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/f64c93d00451e4f1.
Report an issue: GitHub.