{"record":{"id":"f64c93d00451e4f1","repo":"oracle/graal","slug":"field-cannot-be-both-input-and-successor","errorCode":null,"errorMessage":"Field cannot be both input and successor","messagePattern":"Field cannot be both input and successor","errorType":"validation","errorClass":"ElementException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java","lineNumber":123,"sourceCode":"\n    private void scanFields(TypeElement node) {\n        TypeElement currentClazz = node;\n        do {\n            for (VariableElement field : ElementFilter.fieldsIn(currentClazz.getEnclosedElements())) {\n                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\");","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler.processor/src/jdk/graal/compiler/nodeinfo/processor/GraphNodeVerifier.java#L105-L141","documentation":"Thrown by GraphNodeVerifier (part of the Graal node-info annotation processor) when a field in a Node class carries both @Input (or @OptionalInput) and @Successor annotations. Inputs and successors are distinct edge kinds in Graal's graph model with different invariants (inputs have usages, successors form the control-flow topology), so a single field cannot be both. It surfaces as a compile-time ElementException on the offending field.","triggerScenarios":"Declaring a Node field annotated with e.g. @Input @Successor NodeClass field; or @OptionalInput @Successor. The verifier checks annotation mirrors directly on non-static, non-transient fields of classes it processes.","commonSituations":"Copy-paste when adding a successor field from an input field template; refactorings that change an edge's role without removing the old annotation; experimental node classes where the author was unsure which edge kind to use.","solutions":["Decide the edge's role: data/usage edge -> keep @Input/@OptionalInput; control-flow edge -> keep @Successor; remove the other annotation.","If you truly need both relationships, model them as two separate fields.","Re-run the build; the processor re-verifies on the next annotation-processing round."],"exampleFix":"// before\n@Input\n@Successor\nNode next;\n\n// after (control-flow edge -> @Successor only)\n@Successor\nNode next;","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"This is a compile-time ElementException from the node-info processor; there is no runtime catch. CI must fail the build on it.","preventionTips":["Annotate each Node field with exactly one edge kind: @Input, @OptionalInput, or @Successor.","Model genuinely dual relationships as two fields.","Review annotations explicitly whenever changing an edge's role in a node class."],"tags":["graalvm","node-api","annotation-processing","compile-time","graph"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}