oracle/graal · error · ElementException
Input field must not be final
Error message
Input field must not be final
What it means
A single-valued input field (annotated @Input or @OptionalInput, type assignable to Node or an interface, not a NodeInputList) must not be final. The compiler runtime replaces input references in-place when edges are rewired (e.g. during canonicalization or when a node is replaced), so the field must stay assignable. The NodeInfo annotation processor raises this at compile time.
Source
Thrown at compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java:138
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)) {
throw new ElementException(field, "Successor list field must not be final");
}
if (modifiers.contains(PUBLIC)) {
throw new ElementException(field, "Successor list field must not be public");
}
} else {
if (!isAssignableWithErasure(field, Node)) {
throw new ElementException(field, "Successor field must be a Node type");
}
if (modifiers.contains(FINAL)) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Drop final from the input field declaration.
- Keep the field protected or package-private (public is separately rejected).
- Initialize the field in the constructor; rely on updateUsages/replaceAtInput APIs for later rewiring.
- Rebuild to confirm the processor error is gone.
Example fix
// before @Input protected final ValueNode x; // after @Input protected ValueNode x;
Defensive patterns
Strategy: validation
Prevention
- Never mark @Input/@OptionalInput fields final; input edges are rewired by the runtime.
- Disable IDE 'add final where possible' refactorings over node source directories.
- Build with mx after adding inputs to a node so the processor validates immediately.
When it happens
Trigger: scanFields() finds modifiers.contains(FINAL) on an @Input/@OptionalInput field in the non-NodeInputList branch (GraphNodeVerifier.java:137-139). Example: '@Input protected final ValueNode operand;'.
Common situations: Writing a new node with final 'value' fields out of habit from value-object design; IDE 'final where possible' refactorings; converting a DTO into a node; copying a constructor-initialized immutable field pattern.
Related errors
- Input list field must not be final
- Input list field must not be public
- Input field type must be an interface or assignable to Node
- 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/f877e52f662199b7.
Report an issue: GitHub.