From 661249cb9050464de3e6986a8f0b586c456f5541 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 13:11:07 +0000 Subject: [PATCH] fix: tolerate duplicate dev environment creation during concurrent invite acceptance When two invite-accept requests race for the same member, both can observe a project as missing its development environment and attempt to create it. The loser hit the unique constraint on (projectId, slug, orgMemberId) and the resulting P2002 error propagated, failing the accept with an incomplete-setup error even though the environment had already been created. Catch P2002 in the per-project creation loop and treat the project as already provisioned, matching the existing P2002 handling for the org-membership row. Non-P2002 errors still surface the incomplete-setup failure. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Qe3bkKy2Pzgt3E2EE5CMNY --- apps/webapp/app/models/member.server.ts | 38 ++++++++++++++++++------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/apps/webapp/app/models/member.server.ts b/apps/webapp/app/models/member.server.ts index 7cd515014b4..2bce9bb78c0 100644 --- a/apps/webapp/app/models/member.server.ts +++ b/apps/webapp/app/models/member.server.ts @@ -215,16 +215,34 @@ export async function provisionMemberDevelopmentEnvironments({ failedProjectId = project.id; failedProjectIndex = index; - await createEnvironment({ - organization, - project, - type: "DEVELOPMENT", - // We set this true but no backfill (yet!?) so never used - // for dev environments - isBranchableEnvironment: true, - member, - maximumConcurrencyLimit, - }); + try { + await createEnvironment({ + organization, + project, + type: "DEVELOPMENT", + // We set this true but no backfill (yet!?) so never used + // for dev environments + isBranchableEnvironment: true, + member, + maximumConcurrencyLimit, + }); + } catch (error) { + // A concurrent accept (double-clicked Accept, a retry, or the recovery + // path overlapping the normal path) can create this member's + // development environment first. The unique constraint on + // (projectId, slug, orgMemberId) then makes our create fail with P2002. + // The environment already exists, so treat the project as provisioned. + if ( + error instanceof PrismaNamespace.PrismaClientKnownRequestError && + error.code === "P2002" + ) { + createdProjectIds.push(project.id); + failedProjectId = undefined; + failedProjectIndex = undefined; + continue; + } + throw error; + } createdProjectIds.push(project.id); failedProjectId = undefined;