hibernate/hibernate-orm · error · IllegalStateException

Session/EntityManager is closed

Error message

Session/EntityManager is closed

What it means

checkOpen() runs at the head of nearly every Session/EntityManager operation. If the session is closed it marks the ongoing transaction rollback-only (when asked) and throws IllegalStateException. Closing is terminal: every later call on the same session fails identically. This is the standard guard behind 'using a closed EntityManager' failures.

Solutions

  1. Keep all entity access, including lazy navigation, inside the transactional scope that loaded the entities.
  2. Fetch what you need up front (join fetch, EntityGraph, @Fetch) so no session is required after the boundary.
  3. For later processing, pass IDs and reload in a fresh session/transaction instead of reusing the closed one.
  4. If you must continue with loaded instances, open a new session and reattach via session.merge()/lock().

Example fix

// before
List<Order> orders;
try (Session s = sf.openSession()) {
    orders = s.createQuery("from Order", Order.class).list();
}
orders.get(0).getItems().size(); // IllegalStateException: session closed
// after
List<Order> orders;
try (Session s = sf.openSession()) {
    orders = s.createQuery("from Order o join fetch o.items", Order.class).list();
} // data already loaded, safe outside
Defensive patterns

Strategy: validation

Validate before calling

if (!session.isOpen()) {
    session = sessionFactory.openSession(); // or fail explicitly
}
return session.find(Customer.class, id);

Try / catch

try {
    return session.find(Customer.class, id);
} catch (IllegalStateException e) {
    if (!session.isOpen()) {
        try (Session fresh = sessionFactory.openSession()) {
            return fresh.find(Customer.class, id);
        }
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling any session/em method after session.close(); using a container-managed EntityManager after the @Transactional method it belonged to exited; reusing a request-scoped EM in a later thread; keeping an EntityManager in a field/cache across HTTP requests.

Common situations: Lazy loading after the transaction closed (sibling of LazyInitializationException); try-with-resources closing the session before async/later code uses loaded objects; servlet filters closing sessions too early; retry loops reusing the same closed EM; background jobs receiving detached entities.

Related errors


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

Appendix: source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java:1121

	protected void cleanupOnClose() {
		// nothing to do in base impl, here for SessionImpl hook
	}

	@Override
	public boolean isOpenOrWaitingForAutoClose() {
		return !closed && factory.isOpen()
			|| waitingForAutoClose;
	}

	@Override
	public void checkOpen(boolean markForRollbackIfClosed) {
		checkSessionReentrancy();
		if ( isClosed() ) {
			if ( markForRollbackIfClosed && transactionCoordinator.isTransactionActive() ) {
				markForRollbackOnly();
			}
			throw new IllegalStateException( "Session/EntityManager is closed" );
		}
	}

	private void startSessionUseProhibited() {
		sessionUseProhibitedDepth++;
	}

	private void finishSessionUseProhibited() {
		sessionUseProhibitedDepth--;
	}

	protected void checkSessionReentrancy() {
		if ( sessionUseProhibitedDepth > 0 ) {
			throw new IllegalStateException( "Session method called from entity lifecycle callback or Interceptor method" );
		}
	}

	protected void checksBeforeQueryCreation() {

View on GitHub (pinned to fad1729dce)