hibernate/hibernate-orm · error · RollbackException
Transaction was marked for rollback only
Error message
Transaction was marked for rollback only
What it means
jakarta.persistence.RollbackException (javax.persistence pre-Hibernate 6) from the resource-local coordinator's commitRollbackOnly(): the transaction was marked rollback-only — via setRollbackOnly() or by Hibernate after a failed flush — so commit() rolled back instead, and because JPA transaction compliance is enabled (the default in Hibernate 6) that outcome is reported with this exception. The data is rolled back; the message tells you the transaction was poisoned earlier.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java:306
catch (RuntimeException e) {
// commit failed
try {
afterCompletionCallback( StatusTranslator.STATUS_FAILED_COMMIT );
}
catch (RuntimeException e2) {
e.addSuppressed( e2 );
}
throw e;
}
// commit successful
afterCompletionCallback( Status.STATUS_COMMITTED );
}
private void commitRollbackOnly() {
JDBC_LOGGER.onCommitMarkedRollbackOnlyRollingBack();
rollback();
if ( jpaCompliance.isJpaTransactionComplianceEnabled() ) {
throw new RollbackException( "Transaction was marked for rollback only" );
}
}
@Override
public void rollback() {
if ( isActive() ) {
jdbcResourceTransaction.rollback();
afterCompletionCallback( Status.STATUS_ROLLEDBACK );
}
}
@Override
@Nonnull
public TransactionStatus getStatus() {
return jdbcResourceTransaction.getStatus();
}
@OverrideView on GitHub (pinned to fad1729dce)
Solutions
- Treat any persistence exception as terminal: roll back and start a new transaction instead of committing
- In Spring, let the inner exception propagate, or annotate the inner work REQUIRES_NEW when its failure must not poison the outer transaction
- Check tx.getRollbackOnly() before commit and roll back deliberately with a clear application error
- If you intentionally called setRollbackOnly(), call rollback() — never commit()
Example fix
// before
try { em.flush(); } catch (PersistenceException e) { log.warn("continuing", e); }
em.getTransaction().commit(); // RollbackException: Transaction was marked for rollback only
// after
try {
em.flush();
em.getTransaction().commit();
} catch (PersistenceException e) {
if (em.getTransaction().isActive()) em.getTransaction().rollback();
throw e; // never commit a transaction that saw a persistence exception
} Defensive patterns
Strategy: validation
Validate before calling
// decide deliberately before commit
EntityTransaction tx = em.getTransaction();
if (tx.getRollbackOnly()) {
tx.rollback();
throw new IllegalStateException("transaction was marked rollback-only; rolled back cleanly");
}
tx.commit(); Type guard
static boolean isPoisoned(jakarta.persistence.EntityTransaction tx) {
return tx.getRollbackOnly();
} Try / catch
try {
tx.commit();
} catch (jakarta.persistence.RollbackException e) {
// commit already rolled back: close/discard the EntityManager and surface the ORIGINAL
// exception that poisoned the transaction (look at your logs), then retry in a new transaction
} Prevention
- Treat any persistence exception as terminal for its transaction — roll back, never continue
- In Spring, do not catch exceptions thrown by nested @Transactional methods sharing your transaction
- Check getRollbackOnly() at the transaction boundary to fail with a meaningful error
When it happens
Trigger: tx.setRollbackOnly() followed by tx.commit(); an exception during flush/SQL that the application caught and swallowed — Hibernate marks rollback-only — and commit() is attempted anyway; with Spring, an inner @Transactional (default REQUIRED) whose exception was caught by an outer caller marks the shared transaction rollback-only, so the outer commit throws this.
Common situations: Catch-and-continue error handling inside a transaction; Spring nested service calls where the caller swallows a RuntimeException from a transactional callee; JPA compliance enabled by default after upgrading to Hibernate 6 making previously silent rollbacks visible.
Related errors
- Transaction timeout expired
- setRollbackOnly() called on inactive transaction (in JPA com
- getRollbackOnly() called on inactive transaction (in JPA com
- Illegal attempt to specify a SynchronizationType when buildi
- Newer version [" + latestVersion + "] of entity [" + infoStr
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/fb9ddc0fb4905129.
Report an issue: GitHub.