oracle/graal · error · IOException

Expecting

Error message

Expecting 

What it means

While serializing a graph, GraphProtocol writes the announced node count first and then iterates the actual nodes, verifying at the end that it emitted exactly that many ('Expecting N nodes, but found M'). A mismatch means the node set changed during printing — the count and the iteration disagree — which would corrupt the dump stream, so it fails with IOException. The root cause is always concurrent or reentrant graph mutation while the dump runs.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graphio/GraphProtocol.java:555

        final int size = findNodesCount(info);
        writeInt(size);
        int cnt = 0;
        for (Node node : findNodes(info)) {
            NodeClass nodeClass = classForNode(node);
            findNodeProperties(node, props, info);

            writeInt(findNodeId(node));
            writePoolObject(nodeClass);
            writeByte(hasPredecessor(node) ? 1 : 0);
            writeProperties(info, props);
            writeEdges(info, node, true);
            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);

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Dump only quiescent graphs: freeze the graph (or dump at a phase boundary where the framework guarantees stability).
  2. Emit dumps from the compilation thread itself at safe checkpoints rather than asynchronously from another thread.
  3. Ensure custom GraphProtocol hooks (findNodes, getNodes) are pure readers and never mutate the graph.
  4. Catch the IOException per-dump and retry the dump at the next safe point; a failed dump must not break compilation.

Example fix

// before
new Thread(() -> output.print(graph, ...)).start(); // races compilation

// after
// dump on the compilation thread at a phase boundary (graph quiescent)
phaseSuite.addPhase(new Phase() {
    @Override protected void run(StructuredGraph g) { output.print(g, ...); }
});
Defensive patterns

Strategy: try-catch

Validate before calling

// dump only quiescent graphs: trigger from the compilation thread at a phase boundary,
// and freeze the graph for the dump window if your embedding allows it

Try / catch

try {
    output.print(graph, ...);
} catch (IOException e) {
    if (e.getMessage().startsWith("Expecting")) {
        // graph mutated during dump: skip this dump, retry at the next safe checkpoint
        log.fine("Skipped inconsistent graph dump: " + e.getMessage());
    } else throw e;
}

Prevention

When it happens

Trigger: Another thread (or a callback triggered by node accessors like findNodes/findClassForNode) adds or deletes nodes between the count snapshot and the iteration in writeNodes. Dumping a live, still-compiling graph without freezing it. Custom findNodes overrides that lazily modify the graph.

Common situations: Async dump triggers racing an active compilation thread. Debug hooks that mutate the graph from within property/edge suppliers during printing. Dumping at unsafe points where a phase is mid-rewrite.

Related errors


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