oracle/graal · error · IOException
Edge
Error message
Edge
What it means
In the dump protocol, an edge declared 'direct' (a single @Input/@Successor, not a list) must serialize exactly one node reference. writeEdges checks that the Collection returned for a direct edge has size == 1; a null/empty list is tolerated (written as null ref) but size != 1 throws IOException('Edge i in edges is direct, but list isn't singleton'). This almost always indicates inconsistent NodeClass edge metadata versus the node instance's actual data.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graphio/GraphProtocol.java:567
writeEdges(info, node, false);
props.clear();
cnt++;
}
if (size != cnt) {
throw new IOException("Expecting " + size + " nodes, but found " + cnt);
}
}
private void writeEdges(Graph graph, Node node, boolean dumpInputs) throws IOException {
NodeClass clazz = classForNode(node);
Edges edges = findClassEdges(clazz, dumpInputs);
int size = findSize(edges);
for (int i = 0; i < size; i++) {
Collection<? extends Node> list = findNodes(graph, node, edges, i);
if (isDirect(edges, i)) {
if (list != null && list.size() != 1) {
throw new IOException("Edge " + i + " in " + edges + " is direct, but list isn't singleton: " + list);
}
Node n = null;
if (list != null && !list.isEmpty()) {
n = list.iterator().next();
}
writeNodeRef(n);
} else {
if (list == null) {
writeShort((char) 0);
} else {
int listSize = list.size();
if (listSize != ((char) listSize)) {
throw new IOException("Too many nodes in list: " + list.size());
}
writeShort((char) listSize);
for (Node edge : list) {
writeNodeRef(edge);
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Audit the failing node class: every direct @Input/@Successor must be a single Node field, every plural edge a NodeInputList/NodeList field.
- Regenerate/clean build so the NodeClass metadata matches the annotated sources (rebuild the annotation processor output).
- If you override findNodes/findClassEdges in a GraphProtocol subclass, ensure direct edges yield null or a singleton collection.
- Dump with graph verification enabled (VerificationMode) to catch edge inconsistencies before serialization.
Example fix
// before (metadata says direct, field is a list)
public class MyNode extends FixedWithNextNode {
@Input NodeInputList<ValueNode> args; // direct edges must be single Node fields
}
// after
public class MyNode extends FixedWithNextNode {
@Input ValueNode arg; // direct edge: single node
} Defensive patterns
Strategy: try-catch
Validate before calling
// before dumping, verify each node's edges agree with its NodeClass (run with -ea / verification):
// graph.verify() catches direct-vs-list metadata inconsistencies before serialization
if (graph.verify()) {
output.print(graph, ...);
} Try / catch
try {
output.print(graph, ...);
} catch (IOException e) {
if (e.getMessage().contains("is direct, but list isn't singleton")) {
// metadata/field mismatch in a custom node class: fix the node class, not the dumper
throw new IllegalStateException("Node edge metadata inconsistent: " + e.getMessage(), e);
} else throw e;
} Prevention
- Keep @Input/@Successor annotations and field types (Node vs NodeInputList) in exact agreement.
- Clean-build after changing node class annotations so processor-generated NodeClass metadata is regenerated.
- Run graph verification before dumping in development builds.
When it happens
Trigger: A custom Node subclass whose NodeClass describes an edge as direct while the node stores a NodeList for it (or vice versa), so findNodes returns a multi-element collection for a direct edge. Corrupted or mismatched generated NodeClass metadata after partially regenerating node class processors. Custom findNodes/findClassEdges overrides returning the wrong collection.
Common situations: Hand-written node classes where the @Input/@Successor annotations and the field types (Node vs NodeList) disagree. Annotation-processor output out of sync with edited sources (stale generated metadata). Forks that customize edge introspection in GraphProtocol.
Related errors
- Could not load Graal NodeClass TYPE field for
- No class for
- Graph was permanetly frozen.
- unknown verbosity:
- NodeBitMap was modified between the calls to hasNext() and n
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/69523cb6f547d574.
Report an issue: GitHub.