{"record":{"id":"42f083b331d2cc10","repo":"hibernate/hibernate-orm","slug":"cannot-recreate-collection-while-filter-is-enabled-42f083","errorCode":null,"errorMessage":"cannot recreate collection while filter is enabled [%s : %s]","messagePattern":"cannot recreate collection while filter is enabled \\[(.+?) : (.+?)\\]","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/collection/BasicCollectionDecomposer.java","lineNumber":318,"sourceCode":"\t\t\t// Do nothing - we only need to notify the cache\n\t\t}\n\t\telse {\n\t\t\tfinal boolean affectedByFilters = persister.isAffectedByEnabledFilters( session );\n\t\t\tfinal var eventMonitor = session.getEventMonitor();\n\t\t\tfinal var event = eventMonitor.beginCollectionUpdateEvent();\n\t\t\tboolean success = false;\n\t\t\ttry {\n\t\t\t\tif ( !affectedByFilters && collection.empty() ) {\n\t\t\t\t\tif ( !action.isEmptySnapshot() ) {\n\t\t\t\t\t\tvar removeOperation = planRemoveOperation( key, ordinalBase );\n\t\t\t\t\t\tif ( removeOperation != null ) {\n\t\t\t\t\t\t\toperations.add( removeOperation );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ( collection.needsRecreate( persister ) ) {\n\t\t\t\t\tif ( affectedByFilters ) {\n\t\t\t\t\t\tthrow new HibernateException( String.format( Locale.ROOT,\n\t\t\t\t\t\t\t\t\"cannot recreate collection while filter is enabled [%s : %s]\",\n\t\t\t\t\t\t\t\tpersister.getRole(),\n\t\t\t\t\t\t\t\tkey\n\t\t\t\t\t\t) );\n\t\t\t\t\t}\n\t\t\t\t\tif ( !action.isEmptySnapshot() ) {\n\t\t\t\t\t\tvar removeOperation = planRemoveOperation( key, ordinalBase );\n\t\t\t\t\t\tif ( removeOperation != null ) {\n\t\t\t\t\t\t\toperations.add( removeOperation );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Recreate INSERTs use INSERT_OFFSET which is higher than DELETE_OFFSET to avoid unique constraint violations\n\t\t\t\t\toperations.addAll( planRecreateOperation(\n\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tordinalBase,\n\t\t\t\t\t\t\tsession\n\t\t\t\t\t) );","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/collection/BasicCollectionDecomposer.java#L300-L336","documentation":"BasicCollectionDecomposer is the graph-queue counterpart of CollectionUpdateAction for planning collection updates. When the collection needs full recreate and a filter affecting it is enabled (BasicCollectionDecomposer.java:318), it throws HibernateException(\"cannot recreate collection while filter is enabled [role : key]\"). Bag-style collections (unindexed lists, arrays, maps without collection id) cannot be row-diffed, so Hibernate must delete-all and reinsert - unsafe under a filter because filtered rows are invisible and would be lost or wrongly inserted.","triggerScenarios":"Running with the default graph flush queue (8.x), an enabled @Filter affecting the collection role, and a needsRecreate() collection (bag/array/no-id map) that was cleared or bulk-replaced during the session; flush plans the recreate and hits the guard with the role and key in the message.","commonSituations":"Soft-delete/tenant filters combined with @OneToMany List bags; replacing collection contents (clear + addAll) in services while a request-scoped filter is on; migrating from Set to List mappings under existing filters.","solutions":["Re-map the collection as diffable: @OrderColumn indexed list, Set, or @CollectionId (idbag) so needsRecreate() returns false","Disable the filter before mutating: session.disableFilter(\"name\"), flush, re-enable afterwards","Mutate incrementally (add/remove specific elements) instead of clear()/replace when filters are active","Move the @Filter off the collection role if it was never meant to apply there"],"exampleFix":"// before - clearing a bag while a filter is enabled\n@OneToMany(mappedBy = \"user\", cascade = ALL)\nprivate List<Role> roles = new ArrayList<>(); // bag: needs recreate\nuser.getRoles().clear();\nuser.getRoles().addAll(newRoles); // flush -> \"cannot recreate collection while filter is enabled\"\n\n// after - indexed collection diffs rows, no recreate, no guard\n@OneToMany(mappedBy = \"user\", cascade = ALL)\n@OrderColumn(name = \"sort_order\")\nprivate List<Role> roles = new ArrayList<>();","handlingStrategy":"validation","validationCode":"// guard bulk-replacement of a collection under active filters\npublic void replaceRoles(Session session, User user, List<Role> newRoles) {\n    if (session.getEnabledFilter(\"tenant\") != null && needsRecreate(user.getRoles())) {\n        session.disableFilter(\"tenant\");\n        try { user.setRoles(newRoles); session.flush(); }\n        finally { session.enableFilter(\"tenant\").setParameter(\"tid\", currentTenant()); }\n    } else {\n        user.setRoles(newRoles);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    session.flush();\n} catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"cannot recreate collection\")) {\n        // re-map the collection indexed/idbag, or retry with filters disabled\n    } else throw e;\n}","preventionTips":["Prefer @OrderColumn/Set/@CollectionId mappings wherever filters apply to the owner","Treat 'clear + addAll' on filtered bags as a forbidden pattern in code review","Cover filter-enabled collection edits with integration tests"],"tags":["collections","filters","graph-queue","flush","mapping","hibernate"],"backgroundTag":"filtered-collection-mutation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}