oracle/graal · error · GraalError
%s must not kill %s
Error message
%s must not kill %s
What it means
When a node's usage count drops to zero, Graph delivers a ZERO_USAGES NodeEvent to registered NodeEventListeners. After invoking the listener's usagesDroppedToZero callback, the graph guarantees the node must still be alive; if the callback itself killed (removed) the node, this GraalError('%s must not kill %s') is thrown naming the listener and the killed node. It is a contract violation by the listener, not a graph corruption bug.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Graph.java:774
* This method dispatches the event to user-defined triggers. The methods that change the
* graph (typically in Graph and Node) must call this method to dispatch the event.
*
* @param e an event
* @param node the node related to {@code e}
*/
final void event(NodeEvent e, Node node) {
switch (e) {
case CONTROL_FLOW_CHANGED:
controlFlowChanged(node);
break;
case INPUT_CHANGED:
inputChanged(node);
break;
case ZERO_USAGES:
GraalError.guarantee(node.isAlive(), "must be alive");
usagesDroppedToZero(node);
if (!node.isAlive()) {
throw new GraalError("%s must not kill %s", this, node);
}
break;
case NODE_ADDED:
nodeAdded(node);
break;
case NODE_REMOVED:
nodeRemoved(node);
break;
case BEFORE_DECODING_FIELDS:
beforeDecodingFields(node);
break;
case AFTER_DECODING_FIELDS:
afterDecodingFields(node);
break;
}
changed(e, node);
}
View on GitHub (pinned to a66e9ccd1d)
Solutions
- Do not delete the node inside usagesDroppedToZero; only observe it. Defer any deletion to after the event (e.g. collect the node in a list and process it when the event scope closes).
- If you must rewire, replace usages of the node rather than deleting the node itself while inside the callback.
- Check whether you actually need ZERO_USAGES handling at all — NodeEventListener.usagesDroppedToZero has a default no-op implementation; override it only for genuine observation.
Example fix
// before
graph.addNodeEventListener(new NodeEventListener() {
@Override
public void usagesDroppedToZero(Node node) {
node.safeDelete(); // GraalError: listener must not kill node
}
});
// after
List<Node> dead = new ArrayList<>();
graph.addNodeEventListener(new NodeEventListener() {
@Override
public void usagesDroppedToZero(Node node) {
dead.add(node); // observe only; delete later
}
});
// ... after event scope:
// dead.forEach(Node::safeDelete); Defensive patterns
Strategy: validation
Validate before calling
// contract check inside a listener
class ObservingListener implements NodeEventListener {
private final List<Node> zeroUsage = new ArrayList<>();
@Override public void usagesDroppedToZero(Node n) {
// observe only — never mutate/delete here
zeroUsage.add(n);
}
// process zeroUsage after the event scope ends
}
static boolean listenerSafe(NodeEventListener l, Node n) {
l.event(NodeEvent.ZERO_USAGES, n);
return n.isAlive(); // false means the listener violated the contract
} Prevention
- Never delete, replaceAndDelete, or rewire the reported node inside usagesDroppedToZero — only record it.
- Queue nodes observed in ZERO_USAGES and act on them after the NodeEventScope closes.
- Add assertions in tests that your listener leaves every event node alive.
When it happens
Trigger: Implementing a NodeEventListener (e.g. via Graph.addNodeEventListener / NodeEventListener for incremental verification, tracking, or custom cleanup) whose usagesDroppedToZero override removes or replaces the node — for example calling node.replaceAndDelete(), node.safeDelete(), or rewriting inputs in a way that deletes the just-orphaned node inside the callback.
Common situations: Custom instrumentation/verification passes that watch for dead nodes and eagerly delete them; porting listeners written against older Graal where the contract was laxer; debugging utilities that 'clean up' zero-usage nodes inline instead of deferring.
Related errors
- Using add for value numberable node. Consider using either u
- No opcode for %s
- out of registers%s
- Error loading phase plan from %s
- not implemented
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/a5d26b0b4ca49b78.
Report an issue: GitHub.