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.

Solutions

  1. Remove the final modifier from the NodeInputList field (keep it protected or package-private).
  2. If immutability was intended, reconsider: input lists are intentionally mutable so the graph can rewire edges; do not model the field as final.
  3. Ensure the field is initialized in the constructor (e.g. this.values = new NodeInputList<>(this)) since it can no longer be final.
  4. 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

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


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

Appendix: 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)