oracle/graal · error · GraalGraphError

accessing node id in %s across %d graph compression%s

Error message

accessing node id in %s across %d graph compression%s

What it means

Node ids are dense array indices that are renumbered whenever a graph is compressed (StructuredGraph.maybeCompress reuses ids of deleted nodes to keep arrays small). A NodeIdAccessor records the graph's compression counter (epoch) at creation; verifyIdsAreStable() throws GraalGraphError if any compression happened since, because an id captured earlier may now point at a different node. The check runs via assert, so it fires with assertions enabled (the default in Graal development runs).

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeIdAccessor.java:52

    NodeIdAccessor(Graph graph) {
        this.graph = graph;
        this.epoch = graph.compressions;
    }

    Graph getGraph() {
        return graph;
    }

    /**
     * Verifies that node identifiers have not changed since this object was created.
     *
     * @return true if the check succeeds
     * @throws GraalGraphError if the check fails
     */
    boolean verifyIdsAreStable() {
        int compressions = graph.compressions - epoch;
        if (compressions != 0) {
            throw new GraalGraphError("accessing node id in %s across %d graph compression%s", graph, compressions, compressions == 1 ? "" : "s");
        }
        return true;
    }

    /**
     * Gets the identifier for a node. If assertions are enabled, this method asserts that the
     * identifier is stable.
     */
    int getNodeId(Node node) {
        assert verifyIdsAreStable();
        if (!node.isAlive()) {
            throw new InternalError(node.toString());
        }
        return node.id();
    }
}

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Recreate NodeIdAccessor-dependent structures (NodeMap, NodeBitMap) after any point where compression may occur instead of caching them across phases.
  2. Never store raw node ids; store the Node objects or use Node.getId() only for transient logging within one phase.
  3. Register with the graph's compression events / check graph.compressions against your epoch before using ids (mirror verifyIdsAreStable).
  4. As a stopgap for experiments you can disable compression (e.g. StructuredGraph option governing maybeCompress), but fix the lifecycle for real code.

Example fix

// before
NodeMap<T> map = new NodeMap<>(graph);
runOtherPhases(graph); // may compress the graph
map.set(someNode, value); // id epoch mismatch -> GraalGraphError

// after
runOtherPhases(graph);
NodeMap<T> map = new NodeMap<>(graph); // build after the last possible compression
map.set(someNode, value);
Defensive patterns

Strategy: validation

Validate before calling

// NodeIdAccessor is package-private; the caller-level equivalent is to not cache id-dependent state:
int epoch = graph.getCompressions(); // use whatever accessor your Graal version exposes (graph.compressions)
// ... later, before using ids:
if (epoch != graph.getCompressions()) {
    rebuildIdKeyedStructures(); // recreate NodeMap/NodeBitMap now
}

Try / catch

try {
    int id = accessor.getNodeId(node); // throws GraalGraphError via assert when enabled
} catch (GraalGraphError e) {
    // ids stale: rebuild accessor and retry the lookup
    accessor = new NodeIdAccessor(graph);
    id = accessor.getNodeId(node);
}

Prevention

When it happens

Trigger: Creating a NodeIdAccessor (or NodeMap/NodeBitMap keyed by ids) in one phase, then calling getNodeId() after a phase that triggers graph compression between them. Caching raw node ids (int values) across maybeCompress() calls. Long-lived maps built in an early phase and consulted in a late phase.

Common situations: Custom phases that keep NodeMaps alive across phase suites; Graal runs compression at phase-suite boundaries to reclaim id space. Debug code that stashes node ids for later correlation. Timing-dependent hits: only graphs with enough deletions actually compress, so the bug appears intermittently.

Related errors


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