oracle/graal · error · GraalGraphError
this node is not alive:
Error message
this node is not alive:
What it means
NodeMap stores values in an array indexed by the node's id, which is only valid for nodes currently alive in the map's own graph. set(Node, T) therefore throws GraalGraphError when !node.isAlive(), because writing for a dead node would corrupt the id-indexed store or silently alias a recycled id. An assert additionally requires the node to belong to the same graph (node.graph() == graph).
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeMap.java:105
throw new UnsupportedOperationException("isEmpty() is not supported for performance reasons");
}
@Override
public boolean containsKey(Node node) {
if (node.graph() == graph()) {
return get(node) != null;
}
return false;
}
public Graph graph() {
return graph;
}
public void set(Node node, T value) {
assert check(node);
if (!node.isAlive()) {
throw new GraalGraphError("this node is not alive: " + node);
}
values[getNodeId(node)] = value;
}
public void setAndGrow(Node node, T value) {
checkAndGrow(node);
set(node, value);
}
/**
* @param i
* @return Return the key for the entry at index {@code i}
*/
protected Node getKey(int i) {
return graph.getNode(i);
}
@OverrideView on GitHub (pinned to a66e9ccd1d)
Solutions
- Guard every set with node.isAlive() && node.graph() == map.graph().
- Compute values before mutating and write them into a fresh NodeMap created after the mutation completes.
- When working on a copy of the graph, allocate a new NodeMap bound to that copy rather than reusing the original's map.
Example fix
// before
for (Node n : nodes) {
transform(n); // may replace/delete n
map.set(n, result); // GraalGraphError: not alive
}
// after
for (Node n : nodes) {
transform(n);
if (n.isAlive() && n.graph() == map.graph()) {
map.set(n, result);
}
} Defensive patterns
Strategy: validation
Validate before calling
if (node.isAlive() && node.graph() == map.graph()) {
map.set(node, value);
} Type guard
static boolean canStore(NodeMap<?> map, Node node) {
return node != null && node.isAlive() && node.graph() == map.graph();
} Prevention
- Check isAlive() before every set() in loops that also replace or delete nodes.
- Allocate per-graph maps; never reuse one graph's NodeMap against a copy.
- Run with assertions enabled so the same-graph assert surfaces misuse early.
When it happens
Trigger: Calling map.set(node, value) after the node was deleted/replaced by an earlier transform in the same loop or phase. Setting values for nodes belonging to a different graph (e.g. a copied graph) — caught by the assert in debug runs. Iterating a stale node list and writing results into the map.
Common situations: Phases that replace nodes and then annotate them in one pass. Splitting work across phases without rebuilding the NodeMap. Using one graph's map while visiting nodes of a cloned graph (graph copies get fresh ids). Assertion-enabled dev builds also catch cross-graph misuse earlier than production runs.
Related errors
- Graph was permanetly frozen.
- NodeBitMap was modified between the calls to hasNext() and n
- accessing node id in %s across %d graph compression%s
- isEmpty() is not supported for performance reasons
- size() is not supported for performance reasons
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/1b759696e8231c23.
Report an issue: GitHub.