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.

Solutions

  1. Drop final from the input field declaration.
  2. Keep the field protected or package-private (public is separately rejected).
  3. Initialize the field in the constructor; rely on updateUsages/replaceAtInput APIs for later rewiring.
  4. 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

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


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

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