hibernate/hibernate-orm · error · IllegalStateException

Resource-local transaction already active

Error message

Resource-local transaction already active

What it means

The non-JPA branch of TransactionImpl.begin(): when a transaction is already active, jpaCompliance is off, and the coordinator is not JTA (resource-local JDBC), Hibernate still refuses a second begin() with IllegalStateException. Native mode is only slightly more lenient than JPA mode — it never silently joins an existing resource-local transaction.

Source

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

	}

	@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" );
		}
		else {
			CORE_LOGGER.committingTransaction();
			try {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Reuse the current transaction: keep the Transaction from the first begin() or check session.getTransaction().isActive() before calling begin() again
  2. Restructure nested calls so exactly one layer owns begin/commit (template-method or try-with-resources pattern)
  3. Ensure exception paths still commit or roll back, so no active transaction leaks into the next begin()

Example fix

// before
Transaction tx = session.beginTransaction();
processBatchPart1();
Transaction tx2 = session.beginTransaction(); // resource-local -> IllegalStateException

// after
Transaction tx = session.getTransaction();
if (!tx.isActive()) {
    tx = session.beginTransaction();
}
processBatchPart1();
Defensive patterns

Strategy: validation

Validate before calling

Transaction tx = session.getTransaction();
if (!tx.isActive()) {
    tx = session.beginTransaction();
}

Prevention

When it happens

Trigger: session.beginTransaction() called twice in native Hibernate mode with a resource-local (JDBC) transaction coordinator and no commit/rollback between the calls; the second call sees isActive() and a non-JTA coordinator and throws.

Common situations: Defensive begin() calls in utility/service methods that call each other; batch loops that begin per iteration when a commit path is skipped on exception; migration from JTA to resource-local datasources where join semantics were expected.

Related errors


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