hibernate/hibernate-orm · error · IllegalStateException

Session method called from entity lifecycle callback or Inte

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.

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)

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.