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

  1. Audit the failing node class: every direct @Input/@Successor must be a single Node field, every plural edge a NodeInputList/NodeList field.
  2. Regenerate/clean build so the NodeClass metadata matches the annotated sources (rebuild the annotation processor output).
  3. If you override findNodes/findClassEdges in a GraphProtocol subclass, ensure direct edges yield null or a singleton collection.
  4. 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

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


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/69523cb6f547d574. Report an issue: GitHub.