{"record":{"id":"094183c84e66a6a8","repo":"hibernate/hibernate-orm","slug":"found-shared-references-to-a-collection","errorCode":null,"errorMessage":"Found shared references to a collection: {}","messagePattern":"Found shared references to a collection: (.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java","lineNumber":185,"sourceCode":"\t\tfinal boolean isBytecodeEnhanced =\n\t\t\t\tpersister.getOwnerEntityPersister()\n\t\t\t\t\t\t.getBytecodeEnhancementMetadata()\n\t\t\t\t\t\t.isEnhancedForLazyLoading();\n\t\tif ( isBytecodeEnhanced && !collection.wasInitialized() ) {\n\t\t\t// the class of the collection owner is enhanced for lazy loading,\n\t\t\t// and we found an un-initialized PersistentCollection, so skip it\n\t\t\tif ( CORE_LOGGER.isTraceEnabled() ) {\n\t\t\t\tCORE_LOGGER.skippingUninitializedBytecodeLazyCollection(\n\t\t\t\t\t\tcollectionInfoString( persister, collection, collectionEntry.getCurrentKey(), session ) );\n\t\t\t}\n\t\t\tflushProcessingContext.markCollectionReached( collection );\n\t\t\tflushProcessingContext.markCollectionProcessed( collection );\n\t\t}\n\t\t// The reached status is just to detect any silly users\n\t\t// who set up circular or shared references between/to collections.\n\t\telse if ( flushProcessingContext.isCollectionReached( collection ) ) {\n\t\t\t// We've been here before\n\t\t\tthrow new HibernateException( \"Found shared references to a collection: \" + type.getRole() );\n\t\t}\n\t\telse {\n\t\t\tflushProcessingContext.markCollectionReached( collection );\n\t\t\tlogReachedCollection( collection, session, persister, collectionEntry );\n\t\t\tprepareCollectionForUpdate( collection, collectionEntry, factory, flushProcessingContext );\n\t\t}\n\t}\n\n\tprivate static void logReachedCollection(\n\t\t\tPersistentCollection<?> collection,\n\t\t\tSessionImplementor session,\n\t\t\tCollectionPersister persister,\n\t\t\tCollectionEntry collectionEntry) {\n\t\tif ( CORE_LOGGER.isTraceEnabled() ) {\n\t\t\tif ( collection.wasInitialized() ) {\n\t\t\t\tCORE_LOGGER.collectionFoundInitialized(\n\t\t\t\t\t\tcollectionInfoString(\n\t\t\t\t\t\t\t\tpersister,","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java#L167-L203","documentation":"During a single flush, Hibernate tracks every collection it has already reached (flushProcessingContext.markCollectionReached). Reaching the SAME collection instance a second time within one flush cycle means it is referenced from more than one place - typically two entities/properties pointing at one collection, or a circular reference between collections. The reached-check exists specifically to catch this user modeling error.","triggerScenarios":"Two associations (two entities, or two collection-valued properties) referencing the same collection instance within the objects reachable at flush; bidirectional collection wiring that assigns one side's collection object to the other side; shared collections in cached or pooled domain objects. Thrown from processReachableCollection on the second visit.","commonSituations":"Synchronizing both sides of a bidirectional relation by copying the collection reference instead of adding elements; DTO round-trips that put one collection into two parents; session cache/second-level cache returning a shared collection; utility code that 'reuses' collections for memory reasons.","solutions":["Give each association its own collection instance; to sync bidirectional sides use element operations: parent.getChildren().add(c); c.setParent(parent);","Audit code that assigns a collection obtained from a getter (a.get...() returns Hibernate's PersistentCollection - never hand it to another owner).","Clear caches/pools that hold Hibernate-managed collections across requests or entities.","Simplify the model if collections genuinely must be shared - map it as one association with multiple parents (many-to-many)."],"exampleFix":"// before - second assignment shares the same collection instance\ninvoiceA.setItems(invoiceB.getItems());\n// after - copy elements into a fresh collection\ninvoiceA.setItems(new ArrayList<>(invoiceB.getItems()));","handlingStrategy":"validation","validationCode":"// Guard: before wiring two associations, ensure they do not share one instance\nstatic void assertNoSharedInstance(Collection<?> a, Collection<?> b, String role) {\n    if (a != null && a == b) {\n        throw new IllegalArgumentException(\"Shared collection instance for \" + role\n            + \" - copy elements into separate collections\");\n    }\n}","typeGuard":"static boolean isPersistentCollection(Object c) {\n    return c instanceof org.hibernate.collection.spi.PersistentCollection;\n}","tryCatchPattern":"catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Found shared references to a collection\")) {\n        // same collection reached twice in one flush; locate the duplicate reference and copy\n        throw new MappingMisuseException(e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Synchronize bidirectional relations with element add/remove, never by assigning collection references.","Never store a collection obtained from an entity getter inside another entity.","Audit caches/pools that may hand the same collection object to multiple entities."],"tags":["collection","shared-reference","flush","bidirectional","mapping"],"backgroundTag":"shared-collection-instance","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}