{"record":{"id":"07371338ec2e145c","repo":"hibernate/hibernate-orm","slug":"audit-graph-mutation-plan-used-with-non-graph-acti","errorCode":null,"errorMessage":"Audit graph mutation plan used with non-graph action queue","messagePattern":"Audit graph mutation plan used with non-graph action queue","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/collection/AuditCollectionMutationPlanContributor.java","lineNumber":100,"sourceCode":"\t\t\tSessionImplementor session) {\n\t\tfinal var persistenceContext = session.getPersistenceContextInternal();\n\t\tfinal var collectionEntry = persistenceContext.getCollectionEntry( collection );\n\t\tif ( collectionEntry != null && collectionEntry.getLoadedPersister() != null ) {\n\t\t\treturn collection.getStoredSnapshot();\n\t\t}\n\t\telse if ( collection instanceof PersistentArrayHolder<?> ) {\n\t\t\tfinal var oldCollection = persistenceContext.getCollection( new CollectionKey( persister, id ) );\n\t\t\treturn oldCollection != null ? oldCollection.getStoredSnapshot() : null;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate GraphBasedActionQueue resolveCollector(SessionImplementor session) {\n\t\tfinal var actionQueue = session.getActionQueue();\n\t\tif ( actionQueue instanceof GraphBasedActionQueue graphBasedActionQueue ) {\n\t\t\treturn graphBasedActionQueue;\n\t\t}\n\t\tthrow new HibernateException( \"Audit graph mutation plan used with non-graph action queue\" );\n\t}\n}\n","sourceCodeStart":82,"sourceCodeEnd":103,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/collection/AuditCollectionMutationPlanContributor.java#L82-L103","documentation":"AuditCollectionMutationPlanContributor supplies the mutation plan used to audit/temporal-track collection changes, and it needs to register its results on a GraphBasedActionQueue (resolveCollector, AuditCollectionMutationPlanContributor.java:100). If the session's ActionQueue is the legacy implementation (session.getActionQueue() not instanceof GraphBasedActionQueue), it throws HibernateException(\"Audit graph mutation plan used with non-graph action queue\"). The audit/temporal extension and the flush-queue configuration are out of sync: the plan is active but the queue it targets is disabled.","triggerScenarios":"Registering audit/temporal collection mutation plan contributors while hibernate.flush.queue.type=legacy is set (FlushSettings.FLUSH_QUEUE_TYPE); a session type or factory configuration that forces ActionQueueLegacy (e.g. legacy setting in persistence.xml, or an old default carried over from pre-8 config files) while audit plans are on the classpath/enabled.","commonSituations":"Upgrading to Hibernate 8 keeping an explicit hibernate.flush.queue.type=legacy for compatibility, while enabling the audit/history feature that assumes the new default graph queue; mixed configuration across microservice instances; environment-specific property overrides drifting apart.","solutions":["Remove the legacy override or set hibernate.flush.queue.type=graph - the graph queue is the 8.x default and what audit plans require","Verify no programmatic configuration sets a legacy ActionQueueFactory (ActionQueueFactoryService QueueType.LEGACY)","Keep the audit/temporal feature configuration and the flush queue setting in the same config source so they cannot drift","If legacy queue is mandatory for you, disable the audit/graph mutation plan contributor until it supports the legacy queue"],"exampleFix":"# before - conflicting settings: audit plans need the graph queue\nhibernate.flush.queue.type=legacy\nhibernate.audit.enabled=true\n\n# after - align on the graph queue\nhibernate.flush.queue.type=graph\nhibernate.audit.enabled=true","handlingStrategy":"validation","validationCode":"// fail fast at startup if audit plans are on but the queue type is legacy\nString queueType = configuration.getSettings()\n        .getOrDefault(\"hibernate.flush.queue.type\", \"graph\").toString();\nif (auditEnabled && \"legacy\".equals(queueType)) {\n    throw new IllegalStateException(\n        \"audit collection plans require the graph action queue: remove hibernate.flush.queue.type=legacy\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep hibernate.flush.queue.type and audit/temporal settings in one configuration source","Add a startup check that fails configuration loading instead of the first flush","Remember the 8.x default is 'graph' - an explicit 'legacy' is the usual culprit"],"tags":["configuration","audit","graph-queue","flush","hibernate"],"backgroundTag":"flush-queue-config-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}