hibernate/hibernate-orm · error · IllegalStateException

{} is closed

Error message

{} is closed

What it means

AbstractLogicalConnectionImplementor backs the logical JDBC connection of every Hibernate Session/EntityManager. Operations that need the physical transaction handle call errorIfClosed() first, which throws IllegalStateException('<LogicalConnectionImpl...> is closed') once the logical connection - i.e. the session - has been closed. Any use of a closed session's connection/transaction layer trips this guard.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java:39

 * Base support for {@link LogicalConnection} implementations
 *
 * @author Steve Ebersole
 */
public abstract class AbstractLogicalConnectionImplementor implements LogicalConnectionImplementor, PhysicalJdbcTransaction {

	@Nonnull
	private TransactionStatus status = TransactionStatus.NOT_ACTIVE;

	@Override
	@Nonnull
	public PhysicalJdbcTransaction getPhysicalJdbcTransaction() {
		errorIfClosed();
		return this;
	}

	protected void errorIfClosed() {
		if ( !isOpen() ) {
			throw new IllegalStateException( this + " is closed" );
		}
	}

	@Override
	public void afterStatement() {
	}

	@Override
	public void beforeTransactionCompletion() {
	}

	@Override
	public void afterTransaction() {
		getResourceRegistry().releaseResources();
	}

	// PhysicalJdbcTransaction impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View on GitHub (pinned to fad1729dce)

Solutions

  1. Guard with isOpen() before using a possibly-closed session, or restructure to try-with-resources so close is always the last operation.
  2. Complete lazy loading before closing: use join fetch, EntityGraphs, or DTO projections instead of lazy proxies that outlive the session.
  3. Keep EntityManager usage single-threaded and never reuse one after close().
  4. Catch IllegalStateException around the affected block to convert it into a domain-meaningful error (e.g. 'operation on detached data').

Example fix

// before
em.close();
// ... later ...
    em.getTransaction().commit(); // IllegalStateException: LogicalConnection... is closed

// after
try (EntityManager em = emf.createEntityManager()) {
    em.getTransaction().begin();
    // work
    em.getTransaction().commit();
} // close always happens last
Defensive patterns

Strategy: validation

Validate before calling

// guard shared helpers that may run after close
static void requireOpen(EntityManager em) {
    if (!em.isOpen()) {
        throw new IllegalStateException("EntityManager is closed; open a new one for this operation");
    }
}

requireOpen(em);
em.getTransaction().commit();

Try / catch

try {
    em.getTransaction().commit();
} catch (IllegalStateException e) {
    // '<LogicalConnection...> is closed': session was closed before commit
    // treat as a usage bug (closed/detached access), not a retryable failure
    throw new SessionLifecycleException("Operation on closed session", e);
}

Prevention

When it happens

Trigger: Calling getTransaction().begin()/commit(), obtaining the connection, or running any JDBC-level operation on a Session/EntityManager that was already closed; typically lazy loading, commit, or shared-helper access that runs after em.close().

Common situations: Lazy proxies or detached graphs accessed after the session closed; entity managers shared across threads where one thread closes while another still uses it; async processing holding a stale EM; missing try-with-resources so close happens earlier than expected.

Related errors


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