From 13ba5f1aa65a82a21cd159633ed1aaf76636f2d2 Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Thu, 16 Apr 2026 04:51:15 -0400 Subject: [PATCH] fix: prevent intermittent clip splitting failures (#403) Two changes to address the race condition causing "Failed to split clip": Backend (stories.py): Added with_for_update() to the item query in split_story_item so concurrent requests for the same clip are serialized via a row lock instead of racing. Frontend (StoryTrackEditor.tsx): Guard handleSplit with splitItem.isPending to prevent rapid double-clicks from firing multiple mutations before the first completes. Fixes #366 Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> --- app/src/components/StoriesTab/StoryTrackEditor.tsx | 2 +- backend/services/stories.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/components/StoriesTab/StoryTrackEditor.tsx b/app/src/components/StoriesTab/StoryTrackEditor.tsx index ef20bf95..641ad048 100644 --- a/app/src/components/StoriesTab/StoryTrackEditor.tsx +++ b/app/src/components/StoriesTab/StoryTrackEditor.tsx @@ -500,7 +500,7 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) { }, [trimmingItem, trimSide, tempTrimValues, storyId, trimItem, toast]); const handleSplit = useCallback(() => { - if (!selectedClipId) return; + if (!selectedClipId || splitItem.isPending) return; const item = items.find((i) => i.id === selectedClipId); if (!item) return; diff --git a/backend/services/stories.py b/backend/services/stories.py index 611ab29d..cb7a46ef 100644 --- a/backend/services/stories.py +++ b/backend/services/stories.py @@ -484,13 +484,15 @@ async def split_story_item( Returns: List of two updated item details (original and new) or None if not found/invalid """ - # Get the item + # Get the item with a row lock to prevent concurrent splits on the + # same clip (e.g. from rapid double-clicks racing each other). item = ( db.query(DBStoryItem) .filter_by( id=item_id, story_id=story_id, ) + .with_for_update() .first() ) if not item: