From 0f9a3015367ae31c1b79fc264194f1a2975860bb Mon Sep 17 00:00:00 2001 From: can1357 Date: Mon, 13 Jul 2026 18:27:24 +0200 Subject: [PATCH] fix(harbor): fixed Disable dev console mirroring in bun server to prevent AbortError crashes during hot reloads - Disable dev console mirroring in bun server to prevent AbortError crashes during hot reloads. - Verify runner process liveness with process.kill(pid, 0) instead of relying solely on recorded job status. - Force exit marker for jobs marked as running that no longer have an active system process. --- packages/harbor-manager/src/server.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/harbor-manager/src/server.ts b/packages/harbor-manager/src/server.ts index 755e052b7..e9af7650b 100755 --- a/packages/harbor-manager/src/server.ts +++ b/packages/harbor-manager/src/server.ts @@ -182,7 +182,11 @@ export class ManagerServer { // Bun bundles the dashboard (React + TSX) from the HTML import and // serves it on the same port as the API — one process, no Vite. routes: { "/": indexHtml }, - development: process.env.NODE_ENV !== "production" && { hmr: true, console: true }, + // Only `hmr`: Bun's `console: true` mirror opens a server-read stream + // over the dev client that a `--hot` reload's force-close tears down + // mid-read, surfacing an unhandled `AbortError: ERR_STREAM_RELEASE_LOCK` + // that crashes the process. + development: process.env.NODE_ENV !== "production" && { hmr: true }, fetch: request => this.#route(request), }); return this.#server; @@ -380,9 +384,22 @@ export class ManagerServer { if (!run) throw new Error(`run ${jobName} not found`); if (run.benchmark !== "harbor") throw new Error(`resume supports only harbor runs (${jobName} is ${run.benchmark})`); - if (this.#children.has(jobName) || run.status === "running") { + // Trust liveness, not the recorded status: a runner killed while a + // previous server instance owned it leaves a stale `running` row with a + // dead (or null) pid and nobody to fire markExit. + const pidAlive = (pid: number | null): boolean => { + if (pid == null) return false; + try { + process.kill(pid, 0); + return true; + } catch { + return false; + } + }; + if (this.#children.has(jobName) || (run.status === "running" && pidAlive(run.pid))) { throw new Error(`run ${jobName} is already running`); } + if (run.status === "running") this.#store.markExit(jobName, null, true); const jobDir = path.join(this.jobsDir, jobName); if (!fs.existsSync(path.join(jobDir, "config.json"))) { throw new Error(`${jobName} has no harbor config.json to resume from`);