hibernate/hibernate-orm · critical · ConcurrentModificationException

Found a different instance corresponding to instanceId [${in

Error message

Found a different instance corresponding to instanceId [${instanceId}], this might indicate a concurrent access to this persistence context.

What it means

get(int instanceId, Object key) reads the entry slot for instanceId and then verifies slot identity (entry.getKey() == key). Instance ids are unique per enhanced instance inside a persistence context, so finding a different object under the same id means the map was mutated concurrently (or two instances illegally share an id) — Hibernate surfaces this as ConcurrentModificationException with an explicit hint about concurrent persistence-context access. It is a correctness alarm, not a recoverable lookup failure.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/util/collections/InstanceIdentityMap.java:123

	 * @param instanceId the instance id whose associated value is to be returned
	 * @param key key instance to double-check instance equality
	 * @return the value to which the specified instance id is mapped,
	 * or {@code null} if this map contains no mapping for the instance id
	 * @implNote This method accesses the backing array with the provided instance id, but performs an instance
	 * equality check ({@code ==}) with the provided key to ensure it corresponds to the mapped one
	 */
	public @Nullable V get(int instanceId, Object key) {
		if ( instanceId <= 0 ) {
			return null;
		}

		final Entry<K, V> entry = get( instanceId - 1 );
		if ( entry != null ) {
			if ( entry.getKey() == key ) {
				return entry.getValue();
			}
			else {
				throw new ConcurrentModificationException(
						"Found a different instance corresponding to instanceId [" + instanceId +
						"], this might indicate a concurrent access to this persistence context."
				);
			}
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 * @implNote This only works for {@link InstanceIdentity} keys, and it's inefficient
	 * since we need to do a type check. Prefer using {@link #get(int, Object)}.
	 */
	@Override
	public @Nullable V get(Object key) {
		if ( key instanceof InstanceIdentity instance ) {
			return get( instance.$$_hibernate_getInstanceId(), instance );
		}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Audit for Session sharing: one Session per thread, ever (session-per-request, session-per-async-job)
  2. Move parallel entity work to separate Sessions opened inside each worker thread
  3. Wrap external access to the persistence context in proper synchronization if sharing is truly unavoidable
  4. After this exception, discard the Session — its state is suspect; do not continue with it

Example fix

// before: Session shared across threads
List<Order> orders = ids.parallelStream()
        .map( id -> session.get( Order.class, id ) )  // unsafe
        .toList();
// after: each worker uses its own session
List<Order> orders = ids.parallelStream()
        .map( id -> withSession( s -> s.get( Order.class, id ) ) )
        .toList();
Defensive patterns

Strategy: try-catch

Try / catch

try {
    return map.get( instanceId, entity );
} catch ( ConcurrentModificationException e ) {
    // Persistence context corrupted by concurrent access: fail fast,
    // close the session, and surface the thread-safety bug.
    session.close();
    throw new IllegalStateException("Session used concurrently; closing", e);
}

Prevention

When it happens

Trigger: Thread A calls get(id, entity) while thread B put() a different entity under the same instanceId in the same map — i.e. two threads using one Session/persistence context concurrently; also id collisions after manual misuse of the enhancement API.

Common situations: Sharing a Hibernate Session across threads (servlet threads, @Async methods, parallel streams over entity collections calling lazy loads); frameworks that pool or pass Sessions across executor boundaries without synchronization.

Related errors


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