{"record":{"id":"00276cdbef4f5e14","repo":"oracle/graal","slug":"nodebitmap-was-modified-between-the-calls-to-hasne","errorCode":null,"errorMessage":"NodeBitMap was modified between the calls to hasNext() and next()","messagePattern":"NodeBitMap was modified between the calls to hasNext\\(\\) and next\\(\\)","errorType":"exception","errorClass":"ConcurrentModificationException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeBitMap.java","lineNumber":265,"sourceCode":"                currentNodeId = -1;\n            }\n        }\n\n        @Override\n        public boolean hasNext() {\n            if (currentNode == null && currentNodeId >= 0) {\n                forward();\n            }\n            return currentNodeId >= 0;\n        }\n\n        @Override\n        public Node next() {\n            if (!hasNext()) {\n                throw new NoSuchElementException();\n            }\n            if (!currentNode.isAlive()) {\n                throw new ConcurrentModificationException(\"NodeBitMap was modified between the calls to hasNext() and next()\");\n            }\n\n            Node result = currentNode;\n            currentNode = null;\n            return result;\n        }\n\n        @Override\n        public void remove() {\n            throw new UnsupportedOperationException();\n        }\n\n    }\n\n    @Override\n    public Iterator<Node> iterator() {\n        return new MarkedNodeIterator();\n    }","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeBitMap.java#L247-L283","documentation":"NodeBitMap's iterator resolves the 'current node' in hasNext() and hands it out in next(). Between those two calls the iterator re-checks currentNode.isAlive(); if the node was deleted (or the graph otherwise changed), it throws ConcurrentModificationException. This is a fail-fast guard against mutating the graph while iterating a NodeBitMap, since the bitmap is indexed by node id and mutation invalidates that mapping.","triggerScenarios":"Calling iterator.hasNext() then deleting the current node (directly or via a replacing transform) before calling next(). Loops like `for (Node n : bitMap) { n.replaceAndDelete(...); }` where the body mutates the graph. Any node deletion between hasNext() and next() on a NodeBitMap iterator.","commonSituations":"Custom phases that iterate a NodeBitMap (e.g. reachable-node sets, visited sets) and replace/delete nodes in the same loop. Debug passes that clean up nodes while walking a computed set. Porting iteration code from snapshot-based collections to NodeBitMap.","solutions":["Collect the nodes first (e.g. into an ArrayList) and mutate the graph in a second pass after iteration completes.","Use graph.getNodes() iteration semantics or NodeIterable snapshots that tolerate mutation where offered.","Mark nodes in another structure (e.g. a work list) instead of deleting during NodeBitMap iteration.","If you must delete while iterating, use iterator patterns designed for it (explicit while loop over NodeIterable with next() only, never hasNext()+delete+next())."],"exampleFix":"// before\nfor (Node n : bitMap) {\n    if (shouldRemove(n)) {\n        n.replaceAndDelete(replacement); // may kill node between hasNext/next\n    }\n}\n\n// after\nList<Node> toProcess = new ArrayList<>();\nfor (Node n : bitMap) {\n    if (shouldRemove(n)) toProcess.add(n);\n}\nfor (Node n : toProcess) {\n    n.replaceAndDelete(replacement);\n}","handlingStrategy":"try-catch","validationCode":"// structural prevention: snapshot before mutating\nList<Node> snapshot = new ArrayList<>();\nfor (Node n : bitMap) snapshot.add(n);\n// now mutate freely using snapshot","typeGuard":null,"tryCatchPattern":"try {\n    Node n = it.next();\n} catch (ConcurrentModificationException e) {\n    // graph changed mid-iteration: abandon iterator, snapshot, and restart\n    List<Node> snapshot = new ArrayList<>();\n    bitMap.iterator().forEachRemaining(snapshot::add); // may itself fail; rebuild from graph instead\n}","preventionTips":["Adopt a strict two-pass discipline: read/collect during NodeBitMap iteration, mutate after.","Prefer iterator patterns that tolerate deletion when Graal offers them for your iterable.","In tests, run with assertions on and add mutation-during-iteration cases to catch this early."],"tags":["java","graal","graal-compiler","iterator","concurrent-modification","graph"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}