{"record":{"id":"ac516581918133d6","repo":"hibernate/hibernate-orm","slug":"unable-to-perform-beforetransactioncompletion-call","errorCode":null,"errorMessage":"Unable to perform beforeTransactionCompletion callback: {}","messagePattern":"Unable to perform beforeTransactionCompletion callback: (.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/internal/BeforeTransactionCompletionProcessQueue.java","lineNumber":31,"sourceCode":" */\nclass BeforeTransactionCompletionProcessQueue\n\t\textends AbstractTransactionCompletionProcessQueue<BeforeCompletionCallback> {\n\n\tBeforeTransactionCompletionProcessQueue(SharedSessionContractImplementor session) {\n\t\tsuper( session );\n\t}\n\n\tvoid beforeTransactionCompletion() {\n\t\tBeforeCompletionCallback process;\n\t\twhile ( (process = processes.poll()) != null ) {\n\t\t\ttry {\n\t\t\t\tprocess.doBeforeTransactionCompletion( session );\n\t\t\t}\n\t\t\tcatch (HibernateException he) {\n\t\t\t\tthrow he;\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new HibernateException(\n\t\t\t\t\t\t\"Unable to perform beforeTransactionCompletion callback: \" + e.getMessage(), e );\n\t\t\t}\n\t\t}\n\t}\n}\n","sourceCodeStart":13,"sourceCodeEnd":37,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/internal/BeforeTransactionCompletionProcessQueue.java#L13-L37","documentation":"Before a transaction completes, Hibernate drains BeforeTransactionCompletionProcessQueue and runs each BeforeCompletionCallback (flush-time pre-commit hooks, e.g. from interceptors, event listeners, or cache pre-invalidation). HibernateException rethrows as-is; any other exception is wrapped in HibernateException with this message. Unlike the after-completion variant, this fires during pre-commit processing, so the transaction will not commit - expect a rollback.","triggerScenarios":"A registered beforeTransactionCompletion callback throwing a non-Hibernate exception: custom Interceptor.beforeTransactionCompletion, Bean Validation or Envers hooks, entity listeners, timestamp/cache pre-processes. Thrown from beforeTransactionCompletion() during commit, failing the commit.","commonSituations":"Interceptors doing pre-commit validation or audit stamps that hit NPEs/IO errors; version conflicts in custom locking code; listeners assuming state that a rollback path does not provide; errors surfacing only under JTA where beforeCompletion runs on the synchronizer thread.","solutions":["Read the nested cause to find the failing callback and fix it directly - the wrapper only hides its message otherwise.","Make Interceptor.beforeTransactionCompletion implementations exception-safe: catch and log anything that must not block commit.","Move logic that may legitimately fail (external calls) out of pre-commit hooks into post-commit or business code.","Re-run with trace logging on org.hibernate.engine.internal to see which queued process was executing when it threw."],"exampleFix":"// before\n@Override\npublic void beforeTransactionCompletion(Transaction tx) {\n    auditWriter.write(currentAuditRecord());   // IO exception aborts commit\n}\n// after\n@Override\npublic void beforeTransactionCompletion(Transaction tx) {\n    try { auditWriter.write(currentAuditRecord()); }\n    catch (IOException e) { log.error(\"audit pre-write failed\", e); }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    tx.commit();\n} catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Unable to perform beforeTransactionCompletion callback\")) {\n        // commit did NOT happen; inspect e.getCause() for the failing listener/interceptor\n        log.error(\"pre-commit callback failed, rolling back\", e.getCause());\n    }\n    throw e;\n}","preventionTips":["Keep Interceptor.beforeTransactionCompletion free of fallible IO; wrap its body in try/catch.","Unit-test interceptors and event listeners against the commit path, not just the CRUD path.","Enable trace logging for org.hibernate.engine.internal when diagnosing which queued callback threw."],"tags":["transaction","callbacks","interceptor","pre-commit","flush"],"backgroundTag":"transaction-completion-callback-failure","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}