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

  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.