oracle/graal · error · ElementException
Inputs must be either optional or non-optional
Error message
Inputs must be either optional or non-optional
What it means
Thrown by GraphNodeVerifier when a Node field is annotated with both @Input and @OptionalInput simultaneously. Optionality is a single tri-state (absent, optional, non-optional), not a set of flags; annotating both makes the edge's nullability undefined and the generated node plumbing cannot pick one behavior, so the processor rejects it at compile time.
Source
Thrown at compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java:125
TypeElement currentClazz = node;
do {
for (VariableElement field : ElementFilter.fieldsIn(currentClazz.getEnclosedElements())) {
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");
}
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Keep exactly one annotation: @Input for a required edge, @OptionalInput for one that may be null.
- When making an input optional, also audit usages: optional inputs require null checks at use sites and allowedUsage changes.
- Remove the redundant annotation and rebuild so the annotation processor re-validates.
Example fix
// before @Input @OptionalInput ValueNode value; // after @OptionalInput ValueNode value;
Defensive patterns
Strategy: validation
Try / catch
Compile-time annotation-processing error; no runtime catch exists. Fix the field declaration and rebuild.
Prevention
- Use @Input for required edges and @OptionalInput for nullable edges — never both.
- When relaxing an input to optional, delete @Input in the same edit.
- Beware IDE auto-import inserting both annotations during refactors.
When it happens
Trigger: A field declared as @Input @OptionalInput Node value; the verifier sets isNonOptionalInput and isOptionalInput booleans from annotation mirrors and throws when both are true (before the input/successor check would, since that is checked first only for the Successor conflict).
Common situations: Adding @OptionalInput to an existing @Input field during a refactor to permit null, and forgetting to remove @Input; IDE auto-complete inserting both; merging node classes where annotations from two variants accumulate.
Related errors
- Field cannot be both input and successor
- Unexpected tokens :${rule.substring(m.end(), m.regionEnd())}
- didn't consume all tokens
- Out of tokens
- Out of tokens looking for %s
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/1bcab42dd702d412.
Report an issue: GitHub.