hibernate/hibernate-orm · error · HibernateException

Unable to perform beforeTransactionCompletion callback: {}

Error message

Unable to perform beforeTransactionCompletion callback: {}

What it means

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.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/engine/internal/BeforeTransactionCompletionProcessQueue.java:31

 */
class BeforeTransactionCompletionProcessQueue
		extends AbstractTransactionCompletionProcessQueue<BeforeCompletionCallback> {

	BeforeTransactionCompletionProcessQueue(SharedSessionContractImplementor session) {
		super( session );
	}

	void beforeTransactionCompletion() {
		BeforeCompletionCallback process;
		while ( (process = processes.poll()) != null ) {
			try {
				process.doBeforeTransactionCompletion( session );
			}
			catch (HibernateException he) {
				throw he;
			}
			catch (Exception e) {
				throw new HibernateException(
						"Unable to perform beforeTransactionCompletion callback: " + e.getMessage(), e );
			}
		}
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Read the nested cause to find the failing callback and fix it directly - the wrapper only hides its message otherwise.
  2. Make Interceptor.beforeTransactionCompletion implementations exception-safe: catch and log anything that must not block commit.
  3. Move logic that may legitimately fail (external calls) out of pre-commit hooks into post-commit or business code.
  4. Re-run with trace logging on org.hibernate.engine.internal to see which queued process was executing when it threw.

Example fix

// before
@Override
public void beforeTransactionCompletion(Transaction tx) {
    auditWriter.write(currentAuditRecord());   // IO exception aborts commit
}
// after
@Override
public void beforeTransactionCompletion(Transaction tx) {
    try { auditWriter.write(currentAuditRecord()); }
    catch (IOException e) { log.error("audit pre-write failed", e); }
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    tx.commit();
} catch (HibernateException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Unable to perform beforeTransactionCompletion callback")) {
        // commit did NOT happen; inspect e.getCause() for the failing listener/interceptor
        log.error("pre-commit callback failed, rolling back", e.getCause());
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/ac516581918133d6. Report an issue: GitHub.