{"record":{"id":"28c6e6cbc67c9daa","repo":"oracle/graal","slug":"input-list-field-must-not-be-final","errorCode":null,"errorMessage":"Input list field must not be final","messagePattern":"Input list field must not be final","errorType":"validation","errorClass":"ElementException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java","lineNumber":128,"sourceCode":"                Set<Modifier> modifiers = field.getModifiers();\n                if (modifiers.contains(STATIC) || modifiers.contains(TRANSIENT)) {\n                    continue;\n                }\n\n                List<? extends AnnotationMirror> annotations = field.getAnnotationMirrors();\n\n                boolean isNonOptionalInput = findAnnotationMirror(annotations, Input) != null;\n                boolean isOptionalInput = findAnnotationMirror(annotations, OptionalInput) != null;\n                boolean isSuccessor = findAnnotationMirror(annotations, Successor) != null;\n\n                if (isNonOptionalInput || isOptionalInput) {\n                    if (findAnnotationMirror(annotations, Successor) != null) {\n                        throw new ElementException(field, \"Field cannot be both input and successor\");\n                    } else if (isNonOptionalInput && isOptionalInput) {\n                        throw new ElementException(field, \"Inputs must be either optional or non-optional\");\n                    } else if (isAssignableWithErasure(field, NodeInputList)) {\n                        if (modifiers.contains(FINAL)) {\n                            throw new ElementException(field, \"Input list field must not be final\");\n                        }\n                        if (modifiers.contains(PUBLIC)) {\n                            throw new ElementException(field, \"Input list field must not be public\");\n                        }\n                    } else {\n                        if (!isAssignableWithErasure(field, Node) && field.getKind() == ElementKind.INTERFACE) {\n                            throw new ElementException(field, \"Input field type must be an interface or assignable to Node\");\n                        }\n                        if (modifiers.contains(FINAL)) {\n                            throw new ElementException(field, \"Input field must not be final\");\n                        }\n                        if (modifiers.contains(PUBLIC)) {\n                            throw new ElementException(field, \"Input field must not be public\");\n                        }\n                    }\n                } else if (isSuccessor) {\n                    if (isAssignableWithErasure(field, NodeSuccessorList)) {\n                        if (modifiers.contains(FINAL)) {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java#L110-L146","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n@Input\nprotected final NodeInputList<ValueNode> values;\n\n// after\n@Input\nprotected NodeInputList<ValueNode> values;","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["java","graalvm","annotation-processing","graal-node","compile-time"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}