From a4156b2ac6523a1dd62e93babc99a39f9f657c6b Mon Sep 17 00:00:00 2001 From: xCyanGrizzly Date: Wed, 25 Mar 2026 22:45:29 +0100 Subject: [PATCH] fix: add race condition guard and null check in group queries - createOrFindPackageGroup: catch unique constraint violation from concurrent creates and fall back to findFirst - createManualGroup: guard against empty package results before accessing first element Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/telegram/queries.ts | 3 +++ worker/src/db/queries.ts | 31 +++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/lib/telegram/queries.ts b/src/lib/telegram/queries.ts index 60d6530..80745c9 100644 --- a/src/lib/telegram/queries.ts +++ b/src/lib/telegram/queries.ts @@ -609,6 +609,9 @@ export async function createManualGroup(name: string, packageIds: string[]) { where: { id: { in: packageIds } }, select: { sourceChannelId: true }, }); + if (pkgs.length === 0) { + throw new Error("No matching packages found"); + } const channelIds = new Set(pkgs.map((p) => p.sourceChannelId)); if (channelIds.size > 1) { throw new Error("Cannot group packages from different channels"); diff --git a/worker/src/db/queries.ts b/worker/src/db/queries.ts index 9ba80e9..5d34411 100644 --- a/worker/src/db/queries.ts +++ b/worker/src/db/queries.ts @@ -553,16 +553,27 @@ export async function createOrFindPackageGroup(input: { if (existing) return existing.id; - const group = await db.packageGroup.create({ - data: { - mediaAlbumId: input.mediaAlbumId, - sourceChannelId: input.sourceChannelId, - name: input.name, - previewData: input.previewData ? new Uint8Array(input.previewData) : undefined, - }, - }); - - return group.id; + try { + const group = await db.packageGroup.create({ + data: { + mediaAlbumId: input.mediaAlbumId, + sourceChannelId: input.sourceChannelId, + name: input.name, + previewData: input.previewData ? new Uint8Array(input.previewData) : undefined, + }, + }); + return group.id; + } catch (err) { + // Handle race condition: another process created the group between our findFirst and create + if (err instanceof Error && err.message.includes("Unique constraint")) { + const raced = await db.packageGroup.findFirst({ + where: { mediaAlbumId: input.mediaAlbumId, sourceChannelId: input.sourceChannelId }, + select: { id: true }, + }); + if (raced) return raced.id; + } + throw err; + } } export async function linkPackagesToGroup(