oracle/graal · error · ElementException
Successor list field must not be final
Error message
Successor list field must not be final
What it means
A control-flow successor field typed NodeSuccessorList and annotated @Successor must not be final. Successor lists are rewritten whenever the control-flow graph is transformed (e.g. block reordering, node deletion), so the field must remain reassignable. The NodeInfo processor enforces this invariant at annotation-processing time.
Source
Thrown at compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java:147
}
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)) {
throw new ElementException(field, "Successor field must not be final");
}
if (modifiers.contains(PUBLIC)) {
throw new ElementException(field, "Successor field must not be public");
}
}
} else {
if (isAssignableWithErasure(field, Node) && !field.getSimpleName().contentEquals("Null")) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Remove final from the NodeSuccessorList field.
- Initialize the list in the constructor (new NodeSuccessorList<>(this, ...)).
- Keep visibility protected or package-private.
- Rebuild to confirm.
Example fix
// before @Successor protected final NodeSuccessorList<FixedNode> nexts; // after @Successor protected NodeSuccessorList<FixedNode> nexts;
Defensive patterns
Strategy: validation
Prevention
- Treat successor lists as runtime-mutated state: never final.
- Initialize NodeSuccessorList fields in the constructor.
- Use existing fixed/branching nodes as templates for control-flow fields.
When it happens
Trigger: modifiers.contains(FINAL) on a @Successor field whose erased type is assignable to NodeSuccessorList (GraphNodeVerifier.java:146-148). Example: '@Successor protected final NodeSuccessor<FixedNode> successors;'.
Common situations: New control-flow nodes (FixedWithNext successors, branching nodes) written with immutable-field style; applying 'effectively final' refactors; porting node code between Graal versions.
Related errors
- Successor list field must not be public
- Successor field must be a Node type
- Successor field must not be final
- Successor field must not be public
- NodeSuccessorList field must be annotated with @${Successor.
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/78dfe0ecd15410f4.
Report an issue: GitHub.