{"record":{"id":"817fff4fcd549985","repo":"hibernate/hibernate-orm","slug":"could-not-reassociate-uninitialized-transient-coll","errorCode":null,"errorMessage":"Could not reassociate uninitialized transient collection","messagePattern":"Could not reassociate uninitialized transient collection","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/event/internal/ProxyVisitor.java","lineNumber":60,"sourceCode":"\t}\n\n\t/**\n\t * Reattach a detached (disassociated) initialized or uninitialized\n\t * collection wrapper, using a snapshot carried with the collection\n\t * wrapper\n\t */\n\tprotected void reattachCollection(@Nonnull PersistentCollection<?> collection, @Nonnull CollectionType type)\n\t\t\tthrows HibernateException {\n\t\tfinal var session = getSession();\n\t\tfinal var metamodel = session.getFactory().getMappingMetamodel();\n\t\tfinal var context = session.getPersistenceContext();\n\t\tif ( collection.wasInitialized() ) {\n\t\t\tfinal var persister = metamodel.getCollectionDescriptor( type.getRole() );\n\t\t\tcontext.addInitializedDetachedCollection( persister, collection );\n\t\t}\n\t\telse {\n\t\t\tif ( !isCollectionSnapshotValid( collection ) ) {\n\t\t\t\tthrow new HibernateException( \"Could not reassociate uninitialized transient collection\" );\n\t\t\t}\n\t\t\tfinal var persister = metamodel.getCollectionDescriptor( collection.getRole() );\n\t\t\tcontext.addUninitializedDetachedCollection( persister, collection );\n\t\t}\n\t}\n\n}\n","sourceCodeStart":42,"sourceCodeEnd":68,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/event/internal/ProxyVisitor.java#L42-L68","documentation":"HibernateException from ProxyVisitor.reattachCollection: while reattaching a detached entity's collection during lock/refresh-style reattach (the AbstractReattachEventListener proxy visitor path), an uninitialized PersistentCollection failed isCollectionSnapshotValid — its getRole() or getKey() is null. A collection wrapper with no role/key snapshot was never properly associated with a session, i.e. it is effectively transient, so there is nothing to reassociate it with.","triggerScenarios":"session.lock(detachedEntity, LockMode.NONE) (or another reattach-based API) on a detached entity whose uninitialized lazy collection wrapper lost its snapshot — e.g. the collection field was replaced with a bare java.util collection that Hibernate wrapped fresh without a role/key, or a new entity instance with 'new ArrayList<>()' assigned is mistaken for detached state; deserialized/mutated wrappers whose role metadata is gone.","commonSituations":"Calling update()/lock() (reattach semantics) on entities that are actually transient or whose collection fields were swapped after load; mixing merge-style and update-style handling of the same object graph; deep-copy/serialization utilities that drop the PersistentCollection internals; upgrading code that relied on lenient reattachment behavior of older Hibernate versions.","solutions":["Use session.merge(entity) instead of lock()/update() for reattachment — merge copies state onto loaded instances and does not need the old wrapper's snapshot","Ensure detached entities keep their original Hibernate collection instances (don't replace collection fields after load; don't deep-copy wrappers)","For genuinely new entities, save/persist them instead of reattaching; fix the application logic that labels transient instances as detached"],"exampleFix":"// before\n// detached order whose items field was replaced with a plain ArrayList after load\nsession.buildLockRequest(LockOptions.NONE).lock(order); // could not reassociate uninitialized transient collection\n\n// after\nOrder managed = (Order) session.merge(order); // merge copies state, no snapshot needed","handlingStrategy":"fallback","validationCode":"if (order instanceof HibernateProxy || Hibernate.isPropertyInitialized(order, \"items\")) {\n    if (order.getItems() != null && Hibernate.isInitialized(order.getItems())) {\n        // safe to reattach\n    }\n}\n// simplest: ensure managed collections stay attached by using merge() for detached graphs","typeGuard":"boolean safeToReattach(Object entity, String collectionField) {\n    return Hibernate.isPropertyInitialized(entity, collectionField)\n            && Hibernate.isInitialized(getter(entity, collectionField));\n}","tryCatchPattern":"try {\n    session.buildLockRequest(LockOptions.NONE).lock(order);\n} catch (HibernateException e) {\n    if (\"Could not reassociate uninitialized transient collection\".equals(e.getMessage())) {\n        order = (Order) session.merge(order); // fallback: merge does not need the snapshot\n    } else { throw e; }\n}","preventionTips":["Never replace entity collection fields with new java.util instances after load","Use merge() (not update()/lock()) to reattach graphs of uncertain provenance","Initialize needed lazy collections (Hibernate.initialize) before detaching entities across layers"],"tags":["hibernate","reattach","lazy-collection","detached","lock","orm"],"backgroundTag":"detached-collection-reattach","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}