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

InstanceIdentityStore.get(Object key, int instanceId) reads the key slot for instanceId (keys and values occupy adjacent slots: offset and offset+1) and checks k == key. A different object in the slot means the same instance id maps to two distinct instances — only possible if the store/persistence context is accessed concurrently — so it throws ConcurrentModificationException naming the instanceId. It is an integrity alarm, not a normal miss (misses return null).

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/util/collections/InstanceIdentityStore.java:63

	 * @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 int keyIndex = toKeyIndex( instanceId );
		final Page<Object> page = getPage( keyIndex );
		if ( page != null ) {
			final int offset = toPageOffset( keyIndex );
			final Object k = page.get( offset );
			if ( k == key ) {
				//noinspection unchecked
				return (V) page.get( offset + 1 );
			}
			else {
				throw new ConcurrentModificationException(
						"Found a different instance corresponding to instanceId [" + instanceId +
						"], this might indicate a concurrent access to this persistence context."
				);
			}
		}
		return null;
	}

	/**
	 * Associates the specified value with the specified key in this store (optional operation). If the store
	 * previously contained a mapping for the key, the old value is replaced by the specified value.
	 *
	 * @param key key with which the specified value is to be associated
	 * @param value value to be associated with the specified key
	 */
	public void put(Object key, int instanceId, V value) {
		if ( key == null ) {
			throw new NullPointerException( "This store does not support null keys" );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Give each thread its own Session; the persistence context is single-threaded by contract
  2. Synchronize all external access to shared persistence-context state if sharing is unavoidable
  3. Log thread names on every session operation during the investigation to find the second actor
  4. Close the corrupted Session; do not attempt to repair it mid-flight

Example fix

// before
Object cached = store.get( entity, id ); // entity id reused across threads
// after: verify same-instance usage and single-threaded context
assert Thread.currentThread() == ownerThread;
Object cached = ( entity == lastPut ) ? store.get( entity, id ) : null;
Defensive patterns

Strategy: try-catch

Try / catch

try {
    return store.get( key, instanceId );
} catch ( ConcurrentModificationException e ) {
    // instance identity corrupted by concurrent access — abort, close session
    throw new IllegalStateException("Persistence context accessed concurrently", e);
}

Prevention

When it happens

Trigger: Thread B put() a different key under the same instanceId between thread A's slot read and identity check; the store is not internally synchronized, so any cross-thread sharing of the owning persistence context can interleave this way.

Common situations: One Hibernate Session used from multiple threads (parallel streams triggering lazy loads, @Async handlers, hand-rolled pools); tests that hammer a Session from several threads to 'speed up' setup.

Related errors


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