{"record":{"id":"505a97773d6810da","repo":"hibernate/hibernate-orm","slug":"audit-graph-mutation-plan-used-with-non-graph-acti-505a97","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/entity/AuditEntityMutationPlanContributor.java","lineNumber":124,"sourceCode":"\t\t\t\t\t\tstate,\n\t\t\t\t\t\tmodificationType,\n\t\t\t\t\t\tentityAuditSupport\n\t\t\t\t)\n\t\t);\n\t}\n\n\tprivate EntityKey resolveEntityKey(\n\t\t\tObject identifier,\n\t\t\tSharedSessionContractImplementor session) {\n\t\treturn session.generateEntityKey( identifier, entityPersister );\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\tprivate boolean shouldAuditUpdate(int[] dirtyAttributeIndexes, boolean hasDirtyCollection) {\n\t\tif ( dirtyAttributeIndexes == null || dirtyAttributeIndexes.length == 0 ) {\n\t\t\treturn true;\n\t\t}\n\t\tif ( hasDirtyCollection ) {\n\t\t\treturn true;\n\t\t}\n\t\tfor ( int dirtyIndex : dirtyAttributeIndexes ) {\n\t\t\tif ( auditedPropertyMask[dirtyIndex] ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/entity/AuditEntityMutationPlanContributor.java#L106-L142","documentation":"AuditEntityMutationPlanContributor builds the plan for auditing/temporal-tracking entity changes and, like its collection sibling, resolves its collector by casting the session's ActionQueue to GraphBasedActionQueue (AuditEntityMutationPlanContributor.java:124). When the session runs the legacy queue, the instanceof check fails and it throws HibernateException(\"Audit graph mutation plan used with non-graph action queue\"). The entity-audit machinery is enabled but the flush implementation it depends on has been switched off.","triggerScenarios":"Audit/temporal entity plans active in the sameSessionFactory where hibernate.flush.queue.type=legacy is configured; sessions opened from a factory built before the graph setting was corrected; property sources (env var, system property) overriding the queue type back to legacy in one environment.","commonSituations":"Post-8.x upgrades that pinned 'legacy' to avoid other regressions, then turned on the audit feature; staging configs diverging from production; programmatic bootstrap (SessionFactoryBuilder) forgetting to clear the legacy flag.","solutions":["Set hibernate.flush.queue.type=graph (or simply remove the property - graph is the default) so the audit plan gets its GraphBasedActionQueue","Search every property source (persistence.xml, application.yaml, env vars, system properties, programmatic settings) for hibernate.flush.queue.type and remove legacy overrides","Rebuild/restart the SessionFactory after fixing settings - existing sessions keep their old queue type","If legacy must stay, disable the audit entity mutation plan contributor until it supports the legacy queue"],"exampleFix":"// before - legacy queue forced at bootstrap while audit plans are registered\nMap<String,Object> props = Map.of(\"hibernate.flush.queue.type\", \"legacy\");\nEntityManagerFactory emf = Persistence.createEntityManagerFactory(\"auditPu\", props);\n\n// after - use the graph queue required by the audit plan\nMap<String,Object> props = Map.of(\"hibernate.flush.queue.type\", \"graph\");","handlingStrategy":"validation","validationCode":"// assert queue/audit alignment before opening sessions\nif (auditEnabled && !\"graph\".equals(settings.get(\"hibernate.flush.queue.type\"))) {\n    throw new ConfigurationException(\n        \"Audit entity plans need GraphBasedActionQueue; set hibernate.flush.queue.type=graph\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Search all property sources (yaml, env, JVM args, code) for hibernate.flush.queue.type after upgrading","Rebuild the SessionFactory after changing the setting - existing sessions keep the old queue","Add a CI config-diff so staging and production cannot diverge on flush queue settings"],"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"}