Skip to content

Commit 2936b90

Browse files
authored
fix(server): a desktop backend whose launcher died shuts down cleanly instead of crashing (#22471)
1 parent 9892aca commit 2936b90

1 file changed

Lines changed: 11 additions & 0 deletions

File tree

‎apps/server/src/cli/server.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ import { type CliServerFlags, resolveServerConfig, sharedServerCommandFlags } fr
1313

1414
const encodeCommand = Schema.encodeEffect(Schema.fromJsonString(Schema.String));
1515

16+
// The desktop launcher reads this process's stdout and stderr. Once it is gone,
17+
// every log write fails, and Node turns an unhandled stream error into a crash
18+
// that skips shutdown and leaves the ownership lock and runtime record behind.
19+
function ignoreClosedLauncherPipe(error: NodeJS.ErrnoException): void {
20+
if (error.code !== "EPIPE" && error.code !== "ERR_STREAM_DESTROYED") throw error;
21+
}
22+
1623
const runServerCommand = Effect.fn("server.runServerCommand")(function* (
1724
flags: CliServerFlags,
1825
options?: {
@@ -25,6 +32,10 @@ const runServerCommand = Effect.fn("server.runServerCommand")(function* (
2532
const config = yield* resolveServerConfig(flags, logLevel, options);
2633
const run = runServer.pipe(Effect.provideService(ServerConfig.ServerConfig, config));
2734
if (config.mode !== "desktop" || config.desktopBootstrapToken === undefined) return yield* run;
35+
yield* Effect.sync(() => {
36+
process.stdout.on("error", ignoreClosedLauncherPipe);
37+
process.stderr.on("error", ignoreClosedLauncherPipe);
38+
});
2839
return yield* Effect.raceFirst(
2940
run,
3041
awaitLauncherLoss().pipe(

0 commit comments

Comments
 (0)