From 5eac9c584ac67382c71510489578644c81ed01f8 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Sat, 28 Mar 2026 17:56:03 -0400 Subject: [PATCH] fix(cliproxy): guard binary install against ETXTBSY when running In Docker, the dashboard tried to update the CLIProxy binary while the bootstrap's instance was already executing it, causing ETXTBSY. Now catches the error and throws a clear message instead of crashing. --- src/cliproxy/binary/installer.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/cliproxy/binary/installer.ts b/src/cliproxy/binary/installer.ts index 42b1261e..1c951722 100644 --- a/src/cliproxy/binary/installer.ts +++ b/src/cliproxy/binary/installer.ts @@ -35,11 +35,26 @@ export async function downloadAndInstall( fs.mkdirSync(config.binPath, { recursive: true }); - // Delete existing binary before install to prevent mismatched binaries + // Delete existing binary before install to prevent mismatched binaries. + // Skip if binary is currently running (ETXTBSY) — happens in Docker when + // the dashboard tries to update while bootstrap's instance is active. const existingBinary = path.join(config.binPath, getExecutableName(backend)); if (fs.existsSync(existingBinary)) { - fs.unlinkSync(existingBinary); - if (verbose) console.error(`[cliproxy] Removed existing binary: ${existingBinary}`); + try { + fs.unlinkSync(existingBinary); + if (verbose) console.error(`[cliproxy] Removed existing binary: ${existingBinary}`); + } catch (error: unknown) { + const code = + error instanceof Error && 'code' in error ? (error as { code: string }).code : ''; + if (code === 'ETXTBSY' || code === 'EBUSY') { + if (verbose) + console.error(`[cliproxy] Binary is running, skipping update: ${existingBinary}`); + throw new Error( + `CLIProxy binary is currently running and cannot be replaced. Stop the running instance first, or use 'ccs docker update' to update in place.` + ); + } + throw error; + } } const archivePath = path.join(config.binPath, `cliproxy-archive.${platform.extension}`);