{"record":{"id":"1b6e9cc5dd484cbf","repo":"oracle/graal","slug":"expecting","errorCode":null,"errorMessage":"Expecting ","messagePattern":"Expecting ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graphio/GraphProtocol.java","lineNumber":555,"sourceCode":"        final int size = findNodesCount(info);\n        writeInt(size);\n        int cnt = 0;\n        for (Node node : findNodes(info)) {\n            NodeClass nodeClass = classForNode(node);\n            findNodeProperties(node, props, info);\n\n            writeInt(findNodeId(node));\n            writePoolObject(nodeClass);\n            writeByte(hasPredecessor(node) ? 1 : 0);\n            writeProperties(info, props);\n            writeEdges(info, node, true);\n            writeEdges(info, node, false);\n\n            props.clear();\n            cnt++;\n        }\n        if (size != cnt) {\n            throw new IOException(\"Expecting \" + size + \" nodes, but found \" + cnt);\n        }\n    }\n\n    private void writeEdges(Graph graph, Node node, boolean dumpInputs) throws IOException {\n        NodeClass clazz = classForNode(node);\n        Edges edges = findClassEdges(clazz, dumpInputs);\n        int size = findSize(edges);\n        for (int i = 0; i < size; i++) {\n            Collection<? extends Node> list = findNodes(graph, node, edges, i);\n            if (isDirect(edges, i)) {\n                if (list != null && list.size() != 1) {\n                    throw new IOException(\"Edge \" + i + \" in \" + edges + \" is direct, but list isn't singleton: \" + list);\n                }\n                Node n = null;\n                if (list != null && !list.isEmpty()) {\n                    n = list.iterator().next();\n                }\n                writeNodeRef(n);","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graphio/GraphProtocol.java#L537-L573","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Dump only quiescent graphs: freeze the graph (or dump at a phase boundary where the framework guarantees stability).","Emit dumps from the compilation thread itself at safe checkpoints rather than asynchronously from another thread.","Ensure custom GraphProtocol hooks (findNodes, getNodes) are pure readers and never mutate the graph.","Catch the IOException per-dump and retry the dump at the next safe point; a failed dump must not break compilation."],"exampleFix":"// before\nnew Thread(() -> output.print(graph, ...)).start(); // races compilation\n\n// after\n// dump on the compilation thread at a phase boundary (graph quiescent)\nphaseSuite.addPhase(new Phase() {\n    @Override protected void run(StructuredGraph g) { output.print(g, ...); }\n});","handlingStrategy":"try-catch","validationCode":"// dump only quiescent graphs: trigger from the compilation thread at a phase boundary,\n// and freeze the graph for the dump window if your embedding allows it","typeGuard":null,"tryCatchPattern":"try {\n    output.print(graph, ...);\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Expecting\")) {\n        // graph mutated during dump: skip this dump, retry at the next safe checkpoint\n        log.fine(\"Skipped inconsistent graph dump: \" + e.getMessage());\n    } else throw e;\n}","preventionTips":["Never dump from an asynchronous thread while compilation proceeds.","Ensure custom GraphProtocol hooks (findNodes, property suppliers) are read-only.","Treat per-dump IOExceptions as non-fatal: log and continue compiling."],"tags":["java","graal","graphio","concurrency","dumping","io","graph-mutation"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}