oracle/graal · error · ElementException
Data field must be final if public
Error message
Data field must be final if public
What it means
Plain data fields (no @Input/@OptionalInput/@Successor, not Node/NodeInputList/NodeSuccessorList typed) that are public must be final. The node's generated meta-access and substitution logic treats public non-final fields as mutable state outside the graph model, which is unsupported; immutable public constants are fine. This is the classic '@NodeInfo data field' rule checked by GraphNodeVerifier at compile time.
Source
Thrown at compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java:175
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")) {
throw new ElementException(field, "Node field must be annotated with @" + Input.getSimpleName() + ", @" + OptionalInput.getSimpleName() + " or @" + Successor.getSimpleName());
}
if (isAssignableWithErasure(field, NodeInputList)) {
throw new ElementException(field, "NodeInputList field must be annotated with @" + Input.getSimpleName() + " or @" + OptionalInput.getSimpleName());
}
if (isAssignableWithErasure(field, NodeSuccessorList)) {
throw new ElementException(field, "NodeSuccessorList field must be annotated with @" + Successor.getSimpleName());
}
if (modifiers.contains(PUBLIC) && !modifiers.contains(FINAL)) {
throw new ElementException(field, "Data field must be final if public");
}
}
}
currentClazz = getSuperType(currentClazz);
} while (!isObject(getSuperType(currentClazz).asType()));
}
private AnnotationMirror findAnnotationMirror(List<? extends AnnotationMirror> mirrors, TypeElement expectedAnnotationType) {
for (AnnotationMirror mirror : mirrors) {
if (sameType(mirror.getAnnotationType(), expectedAnnotationType.asType())) {
return mirror;
}
}
return null;
}
private boolean isObject(TypeMirror type) {
return sameType(object.asType(), type);View on GitHub (pinned to a66e9ccd1d)
Solutions
- Make the public data field final and initialize it in the constructor.
- Or reduce its visibility to protected/package-private if it must be mutable (and manage mutation via node methods).
- Or model the value as an @Input/@OptionalInput edge if it is actually a node dependency.
- Rebuild to confirm.
Example fix
// before public int offset; // after public final int offset;
Defensive patterns
Strategy: validation
Prevention
- Make public data fields on nodes final and constructor-initialized.
- For mutable node-local state, use protected/package-private fields updated via methods.
When it happens
Trigger: modifiers.contains(PUBLIC) && !modifiers.contains(FINAL) on an unannotated non-node field (GraphNodeVerifier.java:174-176). Example: 'public int offset;' or 'public Stamp stamp;' in a @NodeInfo class.
Common situations: Adding configuration/flag fields to a node and defaulting to public visibility; records-style classes pasted in; exposing cached values publicly without making them final.
Related errors
- Input list field must not be final
- Input list field must not be public
- Input field type must be an interface or assignable to Node
- Input field must not be final
- Input field must not be public
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/0c163aef0812d741.
Report an issue: GitHub.