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);
    }

    @Override

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Guard every set with node.isAlive() && node.graph() == map.graph().
  2. Compute values before mutating and write them into a fresh NodeMap created after the mutation completes.
  3. 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

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


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