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 <[email protected]>
This commit is contained in:
Matt Van Horn
2026-04-16 01:51:15 -07:00
committed by GitHub
co-authored by Matt Van Horn
parent 9a3c307c75
commit 13ba5f1aa6
2 changed files with 4 additions and 2 deletions
@@ -500,7 +500,7 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) {
}, [trimmingItem, trimSide, tempTrimValues, storyId, trimItem, toast]); }, [trimmingItem, trimSide, tempTrimValues, storyId, trimItem, toast]);
const handleSplit = useCallback(() => { const handleSplit = useCallback(() => {
if (!selectedClipId) return; if (!selectedClipId || splitItem.isPending) return;
const item = items.find((i) => i.id === selectedClipId); const item = items.find((i) => i.id === selectedClipId);
if (!item) return; if (!item) return;
+3 -1
View File
@@ -484,13 +484,15 @@ async def split_story_item(
Returns: Returns:
List of two updated item details (original and new) or None if not found/invalid 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 = ( item = (
db.query(DBStoryItem) db.query(DBStoryItem)
.filter_by( .filter_by(
id=item_id, id=item_id,
story_id=story_id, story_id=story_id,
) )
.with_for_update()
.first() .first()
) )
if not item: if not item: