hibernate/hibernate-orm · error · IllegalStateException

Session method called from entity lifecycle callback or…

Error message

Session method called from entity lifecycle callback or Interceptor method

What it means

While Hibernate invokes entity lifecycle callbacks (@PrePersist, @PostLoad, ...) and Interceptor methods it increments sessionUseProhibitedDepth; checkSessionReentrancy() then rejects any reentrant Session API call. JPA forbids EntityManager operations inside lifecycle listeners, and Hibernate enforces it to protect flush-cycle invariants — a session operation from inside a callback would recurse into the flush/persist machinery.

Solutions

  1. Move queries and saves out of the callback into the service/repository method that coordinates the operation.
  2. Inside callbacks, only mutate the entity's own fields or use injected collaborators (CDI/Spring support bean injection into entity listeners) instead of the session.
  3. If cross-entity work is unavoidable, collect it in the listener and perform it after the flush completes (e.g., @PostPersist plus outer service step), or use a dedicated mechanism (Hibernate event system, Envers, domain events).

Example fix

// before
public class AuditListener {
    @PrePersist
    void prePersist(Auditable a) {
        a.setCreatedBy(em.find(User.class, currentUserId())); // IllegalStateException
    }
}
// after
public class AuditListener {
    @PrePersist
    void prePersist(Auditable a) {
        a.setCreatedBy(currentUserProvider().username()); // no session use
    }
}
// cross-entity work moves to the service after save()
Defensive patterns

Strategy: fallback

Validate before calling

// Pattern: never touch the session in a callback; defer work out of it
public class DeferredWorkListener {
    private final Queue<Runnable> pending = new ConcurrentLinkedQueue<>();

    @PreUpdate
    void onUpdate(Auditable a) {
        a.setUpdatedAt(Instant.now());            // allowed: own fields only
        pending.add(() -> counterService.touch(a.getClass())); // no session use here
    }

    public void runPending() { Runnable r; while ((r = pending.poll()) != null) r.run(); } // call after flush, outside callback
}

Prevention

When it happens

Trigger: An entity listener or Interceptor callback calls session/em methods: em.persist/find/query inside @PrePersist/@PostLoad/@PreUpdate, session.flush() or session.get() inside Interceptor.onSave/onFlushDirty/onLoad; also user event listeners calling back into the same session during a flush cycle.

Common situations: Audit listeners resolving the current user with a query; denormalization/counter updates cascading from callbacks; code ported from Hibernate 5 native Session patterns where reentrancy went undetected; @PostLoad enriching entities from other tables.

Related errors


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

Appendix: source

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

		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() {
		checkOpen();
		checkTransactionSyncStatus();
	}

	@Override
	public void prepareForQueryExecution(boolean requiresTxn) {
		checksBeforeQueryCreation();
		if ( requiresTxn && !isTransactionInProgress() ) {
			throw new TransactionRequiredException( "No active transaction" );
		}
	}

	@Override
	@Nullable

View on GitHub (pinned to fad1729dce)