From 1b2f2c71c3c6c406248d52f3e753cdfe5bdd1170 Mon Sep 17 00:00:00 2001
From: souheyl gouadria <118611908+Souheyl7Gouadria@users.noreply.github.com>
Date: Mon, 29 Jun 2026 15:54:15 +0100
Subject: [PATCH] Move filter group destruction after filter operations
(#22248)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### The current order is:
1. Create filter groups
2. Update filter groups
3. Destroy filter groups ← **happens here**
4. Clean up store (cascade)
5. Create/update/delete filters (which may reference groups just
destroyed)
The fix is to move filter group destruction after filter operations, so
filters that reference those groups get created/updated/deleted first.
### after fix : The persistence order is now:
1. Create filter groups
2. Update filter groups
3. Create/update/delete view filters (these can safely reference groups
that still exist)
4. Destroy filter groups (only after all filter mutations are done)
5. Clean up store (cascade-deleted filters
**root cause :** step 4 happened before step 3, so filter
creates/updates would reference groups that had already been deleted in
the same save cycle ; causing the backend to fail with "Migration
execution failed" when it couldn't resolve the `viewFilterGroupId
`foreign key.
this fixes the bug : #21351
Co-authored-by: Souheyl Gouadria
Co-authored-by: Charles Bochet
---
...roupFiltersToViewFiltersAndGroupFilters.ts | 31 ++++++++++---------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts
index 31589686d8..f3b9f67ada 100644
--- a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts
+++ b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts
@@ -89,21 +89,6 @@ export const useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters =
currentView,
);
await performViewFilterGroupAPIUpdate(viewFilterGroupsToUpdate);
- await performViewFilterGroupAPIDestroy(viewFilterGroupIdsToDestroy);
-
- // Mirror the DB cascade: remove cascade-deleted viewFilters from the store
- if (viewFilterGroupIdsToDestroy.length > 0) {
- const destroyedIdsSet = new Set(viewFilterGroupIdsToDestroy);
-
- store.set(metadataStoreState.atomFamily('viewFilters'), (prev) => ({
- ...prev,
- current: (prev.current as FlatViewFilter[]).filter(
- (viewFilter) =>
- !isDefined(viewFilter.viewFilterGroupId) ||
- !destroyedIdsSet.has(viewFilter.viewFilterGroupId),
- ),
- }));
- }
const currentViewFilters = currentView.viewFilters ?? [];
const currentRecordFilters = store.get(
@@ -195,6 +180,22 @@ export const useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters =
if (deleteResult.status === 'failed') {
return;
}
+
+ await performViewFilterGroupAPIDestroy(viewFilterGroupIdsToDestroy);
+
+ // Mirror the DB cascade: remove cascade-deleted viewFilters from the store
+ if (viewFilterGroupIdsToDestroy.length > 0) {
+ const destroyedIdsSet = new Set(viewFilterGroupIdsToDestroy);
+
+ store.set(metadataStoreState.atomFamily('viewFilters'), (prev) => ({
+ ...prev,
+ current: (prev.current as FlatViewFilter[]).filter(
+ (viewFilter) =>
+ !isDefined(viewFilter.viewFilterGroupId) ||
+ !destroyedIdsSet.has(viewFilter.viewFilterGroupId),
+ ),
+ }));
+ }
}, [
canPersistChanges,
currentView,