hibernate/hibernate-orm · error · IllegalStateException

Transaction already active (in JPA compliant mode)

Error message

Transaction already active (in JPA compliant mode)

What it means

TransactionImpl.begin() detects that a transaction is already active. When the session factory runs with JPA transaction compliance enabled (hibernate.jpa.compliance.transaction=true, the default for JPA-bootstrapped factories), re-beginning violates the JPA contract, so an IllegalStateException is thrown instead of joining.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionImpl.java:71

		if ( CORE_LOGGER.isDebugEnabled() && jpaCompliance ) {
			CORE_LOGGER.transactionCreatedInJpaCompliantMode();
		}
	}

	@Override
	public void begin() {
		if ( !session.isOpen() ) {
			throw new IllegalStateException( "Cannot begin Transaction on closed Session/EntityManager" );
		}

		if ( transactionDriverControl == null ) {
			transactionDriverControl =
					transactionCoordinator.getTransactionDriverControl();
		}

		if ( isActive() ) {
			if ( jpaCompliance ) {
				throw new IllegalStateException( "Transaction already active (in JPA compliant mode)" );
			}
			else if ( !transactionCoordinator.getTransactionCoordinatorBuilder().isJta() ) {
				throw new IllegalStateException( "Resource-local transaction already active" );
			}
		}
		else {
			CORE_LOGGER.beginningTransaction();
			transactionDriverControl.begin();
		}
	}

	@Override
	public void commit() {
		// allow MARKED_ROLLBACK to propagate through to transactionDriverControl
		if ( !isActive() ) {
			// we have a transaction that is inactive and has not been marked for rollback only
			throw new IllegalStateException( "Transaction not successfully started" );
		}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Guard with if (!tx.isActive()) tx.begin() so an existing transaction is joined rather than re-begun
  2. Let the container/framework own transaction demarcation (@Transactional) and delete the manual begin()
  3. Ensure every begin() has a matching commit/rollback path so the next begin() starts clean

Example fix

// before
Transaction tx = em.getTransaction();
tx.begin();
doWork();
tx.begin(); // JPA-compliant mode -> IllegalStateException

// after
Transaction tx = em.getTransaction();
if (!tx.isActive()) {
    tx.begin();
}
doWork();
Defensive patterns

Strategy: validation

Validate before calling

if (!tx.isActive()) {
    tx.begin();
}

Prevention

When it happens

Trigger: Calling getTransaction().begin() a second time without an intervening commit/rollback on the same EntityManager while jpaCompliance is on; a manual begin() racing with container- or framework-managed transaction start (@Transactional, JTA-bound EM).

Common situations: Helper methods that 'ensure' a transaction by calling begin() unconditionally inside nested service calls; mixing manual EM transaction control with Spring/JTA-managed transactions; upgrading native Hibernate code to JPA-compliant settings; retry wrappers that restart logic without resetting the EM.

Related errors


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