hibernate/hibernate-orm · error · UnsupportedOperationException

Can't write to a read-only object

Error message

Can't write to a read-only object

What it means

EntityReadOnlyAccess.afterUpdate runs in the post-update phase of flush (with current/previous versions) for cached entities. With the read-only strategy it always throws UnsupportedOperationException because read-only regions never accept writes. Seeing it means an entity cached with usage = read-only went through an update during flush.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/cache/spi/support/EntityReadOnlyAccess.java:93

			@Nonnull SharedSessionContractImplementor session,
			@Nonnull Object key,
			@Nonnull Object value,
			@Nullable Object currentVersion,
			@Nullable Object previousVersion) {
//		LOG.debugf( "Illegal attempt to update item cached as read-only [%s]", key );
		throw new UnsupportedOperationException( "Can't update read-only object" );
	}

	@Override
	public boolean afterUpdate(
			@Nonnull SharedSessionContractImplementor session,
			@Nonnull Object key,
			@Nonnull Object value,
			@Nullable Object currentVersion,
			@Nullable Object previousVersion,
			@Nullable SoftLock lock) {
//		LOG.debugf( "Illegal attempt to update item cached as read-only [%s]", key );
		throw new UnsupportedOperationException( "Can't write to a read-only object" );
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Change the entity's cache usage to READ_WRITE or NONSTRICT_READ_WRITE
  2. Make the entity genuinely immutable and remove the update path
  3. Evict the entity/region before bulk maintenance and reload afterwards
  4. Audit hibernate.cache.default_cache_concurrency_strategy and per-entity @Cache values for mutable classes

Example fix

// before
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class ReferenceData { ... } // later mutated -> afterUpdate throws

// after
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class ReferenceData { ... }
Defensive patterns

Strategy: validation

Validate before calling

org.hibernate.annotations.Cache cache =
        ReferenceData.class.getAnnotation(org.hibernate.annotations.Cache.class);
if (cache != null && cache.usage() == CacheConcurrencyStrategy.READ_ONLY) {
    log.warn("ReferenceData cached read-only: bulk-replace or evict instead of updating");
}

Try / catch

try {
    session.merge(entity);
} catch (UnsupportedOperationException e) {
    if ("Can't write to a read-only object".equals(e.getMessage())) {
        sessionFactory.getCache().evictEntityRegion(entity.getClass());
        throw new IllegalStateException("Change @Cache usage to READ_WRITE for "
                + entity.getClass().getName(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Versioned update of an entity mapped @Cache(usage = READ_ONLY) followed by flush; cascades that dirty a read-only cached entity; global default cache strategy read-only applied to mutable entities.

Common situations: Mutable reference data mapped read-only; optimistic-locking updates touching read-only cached entities; default strategy misconfiguration after a cache rollout.

Related errors


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