oracle/graal · error · ElementException
Input list field must not be final
Error message
Input list field must not be final
What it means
Thrown by the GraalVM NodeInfo annotation processor while verifying a class annotated for the node graph. A non-static field whose type is assignable to NodeInputList and that carries @Input or @OptionalInput must not be declared final. The processor enforces this because input lists are mutated by generated code (Node.replaceAtInput etc.) and by the runtime when inputs are rewired, which a final reference would forbid. It surfaces as a compile-time ElementException pointing at the offending field, walking the whole class hierarchy up to (but excluding) Object.
Source
Thrown at compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java:128
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");
}
}
} else if (isSuccessor) {
if (isAssignableWithErasure(field, NodeSuccessorList)) {
if (modifiers.contains(FINAL)) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Remove the final modifier from the NodeInputList field (keep it protected or package-private).
- If immutability was intended, reconsider: input lists are intentionally mutable so the graph can rewire edges; do not model the field as final.
- Ensure the field is initialized in the constructor (e.g. this.values = new NodeInputList<>(this)) since it can no longer be final.
- Re-run mx build in the compiler suite to confirm the processor accepts the node.
Example fix
// before @Input protected final NodeInputList<ValueNode> values; // after @Input protected NodeInputList<ValueNode> values;
Defensive patterns
Strategy: validation
Prevention
- Follow the node field conventions: @Input/@OptionalInput/@Successor fields are never final and never public.
- Initialize input lists in the constructor with new NodeInputList<>(this, ...) instead of relying on final for immutability.
- Run mx build after every node class edit; the processor reports the exact field immediately.
- Copy a known-good node (e.g. from jdk.graal.compiler.nodes) as the template for new nodes.
When it happens
Trigger: scanFields() in GraphNodeVerifier.java finds a field where findAnnotationMirror(annotations, Input) or OptionalInput is non-null, isAssignableWithErasure(field, NodeInputList) is true, and modifiers.contains(FINAL). Declaring e.g. 'protected final NodeInputList<ValueNode> values' with @Input on it triggers this exact error at annotation-processing time.
Common situations: Writing a new Graal compiler Node and copying plain-data field style (final + public/protected) onto an input list; IDE auto-applying 'make field final' refactors; porting a node from an older Graal version where modifiers differed; adding @Input to a field that was previously un-annotated data.
Related errors
- Input list field must not be public
- Input field type must be an interface or assignable to Node
- Input field must not be final
- Input field must not be public
- Successor list field must not be final
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/28c6e6cbc67c9daa.
Report an issue: GitHub.