hibernate/hibernate-orm · error · IllegalStateException
Cannot mutate immutable graph node
Error message
Cannot mutate immutable graph node
What it means
Entity graphs handed out by the EntityManager (getEntityGraph, findEntityGraphs) and graphs applied to queries are immutable snapshots. Mutating operations (addAttributeNode, addSubgraph, addElementSubgraph, markRemoved, ...) go through verifyMutability(), which throws IllegalStateException when the graph node was constructed with mutable=false.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/graph/internal/AbstractGraphNode.java:27
/**
* @author Steve Ebersole
*/
public abstract class AbstractGraphNode<J> implements GraphNodeImplementor<J> {
private final boolean mutable;
public AbstractGraphNode(boolean mutable) {
this.mutable = mutable;
}
@Override
public boolean isMutable() {
return mutable;
}
protected void verifyMutability() {
if ( !isMutable() ) {
throw new IllegalStateException( "Cannot mutate immutable graph node" );
}
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Copy first: em.createEntityGraph("name") returns a mutable copy of the named graph — mutate that
- With Hibernate's native API, clone via ((GraphImplementor<?>) graph).makeCopy(true) before mutating
- Never mutate graphs returned from getEntityGraph(); build separate named variants instead
- For per-request customization, always start from createEntityGraph and merge from the named graph
Example fix
// before — named graphs from getEntityGraph are immutable
EntityGraph<Order> named = em.getEntityGraph("order.with-items");
named.addAttributeNode("customer"); // IllegalStateException: Cannot mutate immutable graph node
// after — createEntityGraph(name) yields a mutable copy
EntityGraph<Order> copy = em.createEntityGraph("order.with-items");
copy.addAttributeNode("customer");
em.find(Order.class, id, Map.of("jakarta.persistence.fetchgraph", copy)); Defensive patterns
Strategy: type-guard
Type guard
// Hibernate API: only mutate graphs known to be mutable
static boolean isMutable(EntityGraph<?> graph) {
return graph instanceof org.hibernate.graph.spi.GraphImplementor<?> impl && impl.isMutable();
}
// Pure JPA rule: graphs from getEntityGraph()/findEntityGraphs() are read-only;
// graphs from createEntityGraph(...) are mutable. Prevention
- Only mutate graphs obtained from createEntityGraph(...)
- Copy named graphs with createEntityGraph(name) before extending them
- Treat shared/cached graph instances as read-only; build per-use copies
When it happens
Trigger: Fetching a named graph with em.getEntityGraph("name") and then calling addAttributeNode/addSubgraph on it; mutating a graph that was already applied to a query; caching shared graph instances and tweaking them per request.
Common situations: Trying to 'extend' a named graph at runtime; registries that cache graphs and customize them per use case; assuming all graphs are mutable like the ones you create yourself via createEntityGraph.
Related errors
- Duplicate named entity graph '%s'
- The 'root' parameter of the @NamedEntityGraph should be pass
- The 'root' parameter of the @NamedEntityGraph annotation mus
- Not a singular attribute node
- Not a collection-valued attribute node
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/ab6b965f327d3a92.
Report an issue: GitHub.