{"record":{"id":"7c42e1206fd6259d","repo":"hibernate/hibernate-orm","slug":"can-t-write-to-a-read-only-object","errorCode":null,"errorMessage":"Can't write to a read-only object","messagePattern":"Can't write to a read-only object","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/cache/spi/support/EntityReadOnlyAccess.java","lineNumber":93,"sourceCode":"\t\t\t@Nonnull SharedSessionContractImplementor session,\n\t\t\t@Nonnull Object key,\n\t\t\t@Nonnull Object value,\n\t\t\t@Nullable Object currentVersion,\n\t\t\t@Nullable Object previousVersion) {\n//\t\tLOG.debugf( \"Illegal attempt to update item cached as read-only [%s]\", key );\n\t\tthrow new UnsupportedOperationException( \"Can't update read-only object\" );\n\t}\n\n\t@Override\n\tpublic boolean afterUpdate(\n\t\t\t@Nonnull SharedSessionContractImplementor session,\n\t\t\t@Nonnull Object key,\n\t\t\t@Nonnull Object value,\n\t\t\t@Nullable Object currentVersion,\n\t\t\t@Nullable Object previousVersion,\n\t\t\t@Nullable SoftLock lock) {\n//\t\tLOG.debugf( \"Illegal attempt to update item cached as read-only [%s]\", key );\n\t\tthrow new UnsupportedOperationException( \"Can't write to a read-only object\" );\n\t}\n}\n","sourceCodeStart":75,"sourceCodeEnd":96,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/cache/spi/support/EntityReadOnlyAccess.java#L75-L96","documentation":"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.","triggerScenarios":"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.","commonSituations":"Mutable reference data mapped read-only; optimistic-locking updates touching read-only cached entities; default strategy misconfiguration after a cache rollout.","solutions":["Change the entity's cache usage to READ_WRITE or NONSTRICT_READ_WRITE","Make the entity genuinely immutable and remove the update path","Evict the entity/region before bulk maintenance and reload afterwards","Audit hibernate.cache.default_cache_concurrency_strategy and per-entity @Cache values for mutable classes"],"exampleFix":"// before\n@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)\npublic class ReferenceData { ... } // later mutated -> afterUpdate throws\n\n// after\n@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)\npublic class ReferenceData { ... }","handlingStrategy":"validation","validationCode":"org.hibernate.annotations.Cache cache =\n        ReferenceData.class.getAnnotation(org.hibernate.annotations.Cache.class);\nif (cache != null && cache.usage() == CacheConcurrencyStrategy.READ_ONLY) {\n    log.warn(\"ReferenceData cached read-only: bulk-replace or evict instead of updating\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    session.merge(entity);\n} catch (UnsupportedOperationException e) {\n    if (\"Can't write to a read-only object\".equals(e.getMessage())) {\n        sessionFactory.getCache().evictEntityRegion(entity.getClass());\n        throw new IllegalStateException(\"Change @Cache usage to READ_WRITE for \"\n                + entity.getClass().getName(), e);\n    }\n    throw e;\n}","preventionTips":["Treat 'afterUpdate on read-only region' as a mapping bug, not a transient failure","Keep mutable entities out of read-only regions from day one","Cover update flows with integration tests against the production cache strategy"],"tags":["hibernate","second-level-cache","read-only","concurrency-strategy","flush"],"backgroundTag":"write-to-read-only-cache","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}