hibernate/hibernate-orm · error · IllegalStateException
Physical-transaction delegate is no longer valid
Error message
Physical-transaction delegate is no longer valid
What it means
TransactionDriverControlImpl is the per-coordinator handle through which Hibernate's Transaction API drives the JTA adapter; once the coordinator invalidates it (invalidate() sets the flag after the JTA transaction cycle completed), any further begin()/commit()/rollback() throws this IllegalStateException. It means code is driving a Session/Transaction whose JTA transaction already finished - typically a cached Transaction reused after completion, a second commit, or begin() on a stale delegate.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java:419
public TransactionDriverControlImpl(@Nonnull JtaTransactionAdapter jtaTransactionAdapter) {
this.jtaTransactionAdapter = jtaTransactionAdapter;
}
protected void invalidate() {
invalid = true;
}
@Override
public void begin() {
errorIfInvalid();
jtaTransactionAdapter.begin();
joinJtaTransaction();
}
protected void errorIfInvalid() {
if ( invalid ) {
throw new IllegalStateException( "Physical-transaction delegate is no longer valid" );
}
}
@Override
public void commit() {
errorIfInvalid();
getTransactionCoordinatorOwner().flushBeforeTransactionCompletion();
// we don't have to perform any before/after completion processing here. We leave that for
// the Synchronization callbacks
jtaTransactionAdapter.commit();
}
@Override
public void rollback() {
errorIfInvalid();
// we don't have to perform any after completion processing here. We leave that for
// the Synchronization callbacks
jtaTransactionAdapter.rollback();View on GitHub (pinned to fad1729dce)
Solutions
- Use a fresh Session (and its Transaction) per unit of work; never cache Transaction instances
- Check Transaction.getStatus() before begin/commit and re-open the session when the delegate is stale
- Make retry loops begin a new transaction rather than recommitting the old one
- For extended contexts rely on container-managed patterns (OSIV / extended persistence context) instead of manual session reuse
Example fix
// before: transaction cached and reused after completion
private final Transaction tx = session.getTransaction();
void run() {
tx.commit();
tx.begin(); // IllegalStateException: delegate invalidated
}
// after: new session + transaction per unit of work
void run() {
try (Session s = sessionFactory.openSession()) {
s.beginTransaction();
// ... work ...
s.getTransaction().commit();
}
} Defensive patterns
Strategy: validation
Validate before calling
// Check before driving a possibly-stale Transaction
TransactionStatus st = tx.getStatus();
if ( st != TransactionStatus.NOT_ACTIVE && st != TransactionStatus.ACTIVE
&& st != TransactionStatus.MARKED_ROLLBACK ) {
// delegate invalidated by a completed JTA tx: get a fresh session/transaction
session = sessionFactory.openSession();
tx = session.getTransaction();
} Try / catch
try {
tx.begin();
}
catch (IllegalStateException e) {
if ( e.getMessage() != null && e.getMessage().contains("no longer valid") ) {
// stale delegate: open a new session and start a fresh transaction
try (Session fresh = sessionFactory.openSession()) {
fresh.beginTransaction();
// redo the unit of work
}
}
else {
throw e;
}
} Prevention
- Open a new Session per unit of work; never cache Transaction objects in fields or ThreadLocals
- Check Transaction.getStatus() before begin/commit
- Make retry loops start a new transaction instead of reusing the old one
- Close sessions in finally / use try-with-resources so delegates are never reused
When it happens
Trigger: Calling begin(), commit() or rollback() on a Transaction whose delegate was invalidated: tx.commit() invoked twice; a Transaction or Session stored in a field/ThreadLocal and reused across requests; retry logic reusing the same transaction; extended-session patterns on JTA backends.
Common situations: Long-lived Sessions reused across container-managed transactions; cached Transaction objects; catch-and-retry loops that recommit; helpers that begin 'if not active' against a coordinator whose transaction already completed.
Related errors
- Transaction is not accessible when using JTA with JPA-compli
- Exception pulsing TransactionCoordinator
- Explicitly joining a JTA transaction requires a JTA transact
- Unable to start isolated transaction
- Error performing work
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/7fe3d7e862519ad0.
Report an issue: GitHub.