Keep the daemon running when nobody reads its output (#6117)

* fix(server): keep the daemon running when its stdout reader is gone

`paseo daemon run` in the foreground hands its stdout and stderr to the
supervisor. When the program that started it closed its end, as
`paseo daemon run | head -1` does, the supervisor's next write failed
with EPIPE and nothing handled the error. The supervisor exited 1, the
worker lost its parent and exited too, and the CLI reported "Daemon
failed to start".

The supervisor now drops output nobody reads and keeps supervising the
worker. daemon.log still records everything. Other stdout and stderr
errors surface as before.

Closes #6114

* test(server): fail on unexpected daemon.log read errors and check stderr reaches the log

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
paseo-bot[bot]
2026-10-05 15:57:59 +02:00
committed by GitHub
parent dfb8a1a2bc
commit 9da676d709
2 changed files with 52 additions and 1 deletions
+11
View File
@@ -166,6 +166,15 @@ function createDurableLog(
};
}
// The program that started the daemon can close its end of stdout or stderr, as
// `paseo daemon run | head -1` does. Drop the output nobody reads and keep supervising
// the worker; daemon.log still records it.
function ignoreClosedOutput(error: NodeJS.ErrnoException): void {
if (error.code !== "EPIPE") {
throw error;
}
}
export function runSupervisor(options: SupervisorOptions): SupervisorController {
const restartOnCrash = options.restartOnCrash ?? false;
const workerArgs = options.workerArgs ?? process.argv.slice(2);
@@ -482,6 +491,8 @@ export function runSupervisor(options: SupervisorOptions): SupervisorController
process.on("SIGINT", () => forwardSignal("SIGINT"));
process.on("SIGTERM", () => forwardSignal("SIGTERM"));
process.stdout.on("error", ignoreClosedOutput);
process.stderr.on("error", ignoreClosedOutput);
process.stdout.write(`[${options.name}] ${options.startupMessage}\n`);
writeLifecycleLog(options.startupMessage);