From 21a1e97a81d5321e901284cfdad85f21bb73c7c9 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sat, 4 Apr 2026 22:57:25 -0700 Subject: [PATCH] fix(server): prevent identifier collision in issue creation Use GREATEST(counter, MAX(issue_number)) + 1 when incrementing the company issue counter. This self-corrects any desync between the companies.issue_counter column and the actual max issues.issue_number, preventing duplicate key violations on the identifier unique index. Fixes #2705 --- server/src/services/issues.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index 832c4e17..e279ed38 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -1183,9 +1183,19 @@ export function issueService(db: Db) { if (executionWorkspaceId) { await assertValidExecutionWorkspace(companyId, issueData.projectId, executionWorkspaceId, tx); } + // Self-correcting counter: use MAX(issue_number) + 1 if the counter + // has drifted below the actual max, preventing identifier collisions. + const [maxRow] = await tx + .select({ maxNum: sql`coalesce(max(${issues.issueNumber}), 0)` }) + .from(issues) + .where(eq(issues.companyId, companyId)); + const currentMax = maxRow?.maxNum ?? 0; + const [company] = await tx .update(companies) - .set({ issueCounter: sql`${companies.issueCounter} + 1` }) + .set({ + issueCounter: sql`greatest(${companies.issueCounter}, ${currentMax}) + 1`, + }) .where(eq(companies.id, companyId)) .returning({ issueCounter: companies.issueCounter, issuePrefix: companies.issuePrefix });