From 2a54becd27954ffba1b5875b1fbe189e4e0d45aa Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 16 May 2026 05:05:25 +0100 Subject: [PATCH] fix: retry media materialization after cleanup race --- src/media/store.test.ts | 31 +++++++++++++++++++++++++++++++ src/media/store.ts | 24 +++++++++++++----------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/media/store.test.ts b/src/media/store.test.ts index b4fbcec43b7..bbaeee9dee0 100644 --- a/src/media/store.test.ts +++ b/src/media/store.test.ts @@ -303,6 +303,37 @@ describe("media store", () => { }); }, }, + { + name: "retries buffer materialization when cleanup prunes the target dir", + run: async () => { + await withTempStore(async (store) => { + const originalMkdir = fs.mkdir.bind(fs); + let pruned = false; + vi.spyOn(fs, "mkdir").mockImplementation(async (...args) => { + const result = await originalMkdir(...args); + const target = args[0]; + if ( + !pruned && + typeof target === "string" && + path.normalize(target).endsWith(`${path.sep}media${path.sep}materialization-race`) + ) { + pruned = true; + await fs.rm(target, { force: true, recursive: true }); + } + return result; + }); + + const saved = await store.saveMediaBuffer( + Buffer.from("race bytes"), + "text/plain", + "materialization-race", + ); + + expect(pruned).toBe(true); + await expect(fs.readFile(saved.path, "utf8")).resolves.toBe("race bytes"); + }); + }, + }, { name: "rejects traversal media subdirs before saving buffers", run: async () => { diff --git a/src/media/store.ts b/src/media/store.ts index 0a068cdc3b5..aaa8568e77c 100644 --- a/src/media/store.ts +++ b/src/media/store.ts @@ -661,18 +661,20 @@ async function materializeMediaBufferPath(params: { buffer: Buffer; }): Promise { const dir = resolveMediaScopedDir(params.subdir, "materializeMediaBufferPath"); - await fs.mkdir(dir, { recursive: true, mode: 0o700 }); - const written = await writeSiblingTempFile({ - dir, - mode: MEDIA_FILE_MODE, - tempPrefix: `.${params.id}`, - writeTemp: async (tempPath) => { - await fs.writeFile(tempPath, params.buffer, { mode: MEDIA_FILE_MODE }); - return undefined; - }, - resolveFinalPath: () => path.join(dir, params.id), + return await retryAfterRecreatingDir(dir, async () => { + await fs.mkdir(dir, { recursive: true, mode: 0o700 }); + const written = await writeSiblingTempFile({ + dir, + mode: MEDIA_FILE_MODE, + tempPrefix: `.${params.id}`, + writeTemp: async (tempPath) => { + await fs.writeFile(tempPath, params.buffer, { mode: MEDIA_FILE_MODE }); + return undefined; + }, + resolveFinalPath: () => path.join(dir, params.id), + }); + return written.filePath; }); - return written.filePath; } async function writeMediaStreamToFile(params: {