From 38b6d5dfa6ee7bbfa0aa8a888cda02a8122158fc Mon Sep 17 00:00:00 2001 From: CTO Agent Date: Sat, 28 Mar 2026 23:23:18 +0000 Subject: [PATCH 1/2] fix: prevent Vercel Blob upload conflicts and improve error handling - Add addRandomSuffix and allowOverwrite to blob upload token options, preventing "blob already exists" errors on retry uploads - Use Promise.allSettled for resilient multi-file blob uploads - Surface meaningful error messages when blob uploads fail Co-Authored-By: Paperclip --- app/api/sandbox/upload/route.ts | 2 ++ lib/sandboxes/uploadSandboxFiles.ts | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/api/sandbox/upload/route.ts b/app/api/sandbox/upload/route.ts index 48f2395c5..72af0b379 100644 --- a/app/api/sandbox/upload/route.ts +++ b/app/api/sandbox/upload/route.ts @@ -25,6 +25,8 @@ export async function POST(request: Request): Promise { return { maximumSizeInBytes: 100 * 1024 * 1024, // 100MB + addRandomSuffix: true, + allowOverwrite: true, }; }, onUploadCompleted: async () => {}, diff --git a/lib/sandboxes/uploadSandboxFiles.ts b/lib/sandboxes/uploadSandboxFiles.ts index d0e3f56ca..006c08848 100644 --- a/lib/sandboxes/uploadSandboxFiles.ts +++ b/lib/sandboxes/uploadSandboxFiles.ts @@ -36,7 +36,7 @@ export async function uploadSandboxFiles({ path?: string; message?: string; }): Promise<{ uploaded: UploadedFile[]; errors?: string[] }> { - const blobFiles = await Promise.all( + const results = await Promise.allSettled( files.map(async (file) => { const blob = await upload(file.name, file, { access: "public", @@ -47,6 +47,23 @@ export async function uploadSandboxFiles({ }), ); + const blobFiles = results + .filter( + (r): r is PromiseFulfilledResult<{ url: string; name: string }> => + r.status === "fulfilled", + ) + .map((r) => r.value); + + if (blobFiles.length === 0) { + const firstError = results.find((r) => r.status === "rejected") as + | PromiseRejectedResult + | undefined; + throw new Error( + firstError?.reason?.message || + "Failed to upload files to temporary storage", + ); + } + const response = await fetch(`${NEW_API_BASE_URL}/api/sandboxes/files`, { method: "POST", headers: { From cc51c083916685379f0e48271f5a1ce38fda48a2 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Sun, 29 Mar 2026 21:37:34 -0500 Subject: [PATCH 2/2] fix: remove redundant allowOverwrite and surface blob upload errors - Remove allowOverwrite: true (redundant with addRandomSuffix) - Surface rejected blob uploads in the errors array so callers can report partial failures to the user Co-Authored-By: Claude Opus 4.6 (1M context) --- app/api/sandbox/upload/route.ts | 1 - lib/sandboxes/uploadSandboxFiles.ts | 26 ++++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/api/sandbox/upload/route.ts b/app/api/sandbox/upload/route.ts index 72af0b379..2fb52414b 100644 --- a/app/api/sandbox/upload/route.ts +++ b/app/api/sandbox/upload/route.ts @@ -26,7 +26,6 @@ export async function POST(request: Request): Promise { return { maximumSizeInBytes: 100 * 1024 * 1024, // 100MB addRandomSuffix: true, - allowOverwrite: true, }; }, onUploadCompleted: async () => {}, diff --git a/lib/sandboxes/uploadSandboxFiles.ts b/lib/sandboxes/uploadSandboxFiles.ts index 006c08848..c7db2d161 100644 --- a/lib/sandboxes/uploadSandboxFiles.ts +++ b/lib/sandboxes/uploadSandboxFiles.ts @@ -47,20 +47,20 @@ export async function uploadSandboxFiles({ }), ); - const blobFiles = results - .filter( - (r): r is PromiseFulfilledResult<{ url: string; name: string }> => - r.status === "fulfilled", - ) - .map((r) => r.value); + const blobFiles: { url: string; name: string }[] = []; + const blobErrors: string[] = []; + + results.forEach((r, i) => { + if (r.status === "fulfilled") { + blobFiles.push(r.value); + } else { + blobErrors.push(`${files[i].name}: ${r.reason?.message || "Upload failed"}`); + } + }); if (blobFiles.length === 0) { - const firstError = results.find((r) => r.status === "rejected") as - | PromiseRejectedResult - | undefined; throw new Error( - firstError?.reason?.message || - "Failed to upload files to temporary storage", + blobErrors[0] || "Failed to upload files to temporary storage", ); } @@ -83,8 +83,10 @@ export async function uploadSandboxFiles({ throw new Error(data.error || "Failed to upload files"); } + const allErrors = [...blobErrors, ...(data.errors || [])]; + return { uploaded: data.uploaded || [], - errors: data.errors, + ...(allErrors.length > 0 && { errors: allErrors }), }; }