{"record":{"id":"ab6b965f327d3a92","repo":"hibernate/hibernate-orm","slug":"cannot-mutate-immutable-graph-node","errorCode":null,"errorMessage":"Cannot mutate immutable graph node","messagePattern":"Cannot mutate immutable graph node","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/graph/internal/AbstractGraphNode.java","lineNumber":27,"sourceCode":"/**\n * @author Steve Ebersole\n */\npublic abstract class AbstractGraphNode<J> implements GraphNodeImplementor<J> {\n\n\tprivate final boolean mutable;\n\n\tpublic AbstractGraphNode(boolean mutable) {\n\t\tthis.mutable = mutable;\n\t}\n\n\t@Override\n\tpublic boolean isMutable() {\n\t\treturn mutable;\n\t}\n\n\tprotected void verifyMutability() {\n\t\tif ( !isMutable() ) {\n\t\t\tthrow new IllegalStateException( \"Cannot mutate immutable graph node\" );\n\t\t}\n\t}\n}\n","sourceCodeStart":9,"sourceCodeEnd":31,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/graph/internal/AbstractGraphNode.java#L9-L31","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before — named graphs from getEntityGraph are immutable\nEntityGraph<Order> named = em.getEntityGraph(\"order.with-items\");\nnamed.addAttributeNode(\"customer\");        // IllegalStateException: Cannot mutate immutable graph node\n\n// after — createEntityGraph(name) yields a mutable copy\nEntityGraph<Order> copy = em.createEntityGraph(\"order.with-items\");\ncopy.addAttributeNode(\"customer\");\nem.find(Order.class, id, Map.of(\"jakarta.persistence.fetchgraph\", copy));","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Hibernate API: only mutate graphs known to be mutable\nstatic boolean isMutable(EntityGraph<?> graph) {\n    return graph instanceof org.hibernate.graph.spi.GraphImplementor<?> impl && impl.isMutable();\n}\n\n// Pure JPA rule: graphs from getEntityGraph()/findEntityGraphs() are read-only;\n// graphs from createEntityGraph(...) are mutable.","tryCatchPattern":null,"preventionTips":["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"],"tags":["hibernate","entity-graph","immutability","jpa","runtime"],"backgroundTag":"immutable-entity-graph-mutation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}