hibernate/hibernate-orm · error · HibernateException

Unable to perform afterTransactionCompletion callback: {}

Error message

Unable to perform afterTransactionCompletion callback: {}

What it means

After a transaction completes, Hibernate runs registered AfterCompletionCallback instances (cache evictions, post-commit hooks from interceptors/listeners). callAfterCompletion lets CacheException escape quietly (logged, loop continues) but wraps any other exception in HibernateException with this message. Note the timing: the transaction has already committed or rolled back, so this exception surfaces from the completion machinery (often the JTA/resource synchronizer), not from commit() itself.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java:80

					iterator.remove();
				}
			}
		}
		return hasPendingBulkOperationCleanUpActions;
	}

	private boolean callAfterCompletion(boolean success, AfterCompletionCallback process) {
		try {
			process.doAfterTransactionCompletion( success, session );
			return true;
		}
		catch (CacheException ce) {
			CORE_LOGGER.unableToReleaseCacheLock( ce );
			// continue loop
			return false;
		}
		catch (Exception e) {
			throw new HibernateException(
					"Unable to perform afterTransactionCompletion callback: " + e.getMessage(), e );
		}
	}

	private void invalidateCaches() {
		final var factory = session.getFactory();
		if ( factory.getSessionFactoryOptions().isQueryCacheEnabled() ) {
			factory.getCache().getTimestampsCache().
					invalidate( querySpacesToInvalidate.toArray( EMPTY_STRING_ARRAY ), session );
		}
		querySpacesToInvalidate.clear();
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Inspect the nested cause - it identifies which callback failed; fix that callback's own error handling.
  2. Make custom afterTransactionCompletion implementations defensive: catch their own exceptions and log instead of propagating.
  3. If the cause is cache-related, check the second-level cache provider's health/configuration (CacheException is tolerated, other provider runtime exceptions are not).
  4. Keep after-commit side effects out of interceptors - use Spring @TransactionalEventListener(AFTER_COMMIT) or explicit post-commit queues that retry.

Example fix

// before - interceptor callback can throw and abort completion processing
public class NotifyInterceptor implements Interceptor {
    @Override
    public void afterTransactionCompletion(Transaction tx) {
        messageBus.publish(buildNotification()); // throws -> HibernateException
    }
}
// after - swallow and log inside user callback
    @Override
    public void afterTransactionCompletion(Transaction tx) {
        try { messageBus.publish(buildNotification()); }
        catch (Exception e) { log.error("post-commit notify failed", e); }
    }
Defensive patterns

Strategy: try-catch

Try / catch

// Completion failures surface after commit; isolate them at the synchronization boundary
try {
    transaction.commit(); // or let JTA drive completion
} finally {
    // callbacks already ran; if a HibernateException 'Unable to perform afterTransactionCompletion callback'
    // escaped, the data IS committed - log and queue a repair instead of retrying the business operation
}

Prevention

When it happens

Trigger: A registered afterCompletion callback throwing - custom Interceptor.afterTransactionCompletion, event listeners, Envers/audit hooks, or second-level cache operations that fail with something other than CacheException (e.g. NPE in user code, failed notification of a clustered cache). Thrown while the session processes completion, aborting the remaining queued callbacks.

Common situations: Custom interceptors doing post-commit notifications (message publishing, file writes) that throw; cache providers (Infinispan/Ehcache) with connectivity problems surfacing as non-CacheException; errors thrown from afterCompletion in tests with TransactionUtil; Envers listeners misbehaving after commit.

Related errors


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