fix(process): retain failures through finalization

This commit is contained in:
morluto committed 2026-10-27 07:35:13 +08:00
1 parent 381004b664
commit 95d9d51f72
4 files changed
+149 -9

No files matched your search

+10 -2
View File
@@ -220,6 +220,7 @@ const startStoppableCommand = (
}
let stopped: "abort" | "timeout" | "output-limit" | undefined;
let processError: Error | null = null;
let rootFailed = false;
let outputError: Error | undefined;
let deadline: NodeJS.Timeout | undefined;
let grace: NodeJS.Timeout | undefined;
@@ -233,7 +234,7 @@ const startStoppableCommand = (
});
// The first stop request names the failure; a later one is not resent.
const stop = (reason: "abort" | "timeout" | "output-limit"): void => {
if (stopped !== undefined) return;
if (stopped !== undefined || rootFailed || processError !== null) return;
stopped = reason;
child.kill(stopSignal);
grace = setTimeout(() => child.kill("SIGKILL"), STOP_GRACE_MS);
@@ -268,6 +269,11 @@ const startStoppableCommand = (
child.on("error", (error: Error) => {
processError ??= error;
});
child.on("exit", (code, exitSignal) => {
// A failed root has completed its outcome even while descendants retain
// its pipes. Finalization cancellation cannot replace that failure.
rootFailed = code !== 0 || exitSignal !== null;
});
child.on("close", (code, exitSignal) => {
signal?.removeEventListener("abort", onAbort);
clearTimeout(deadline);
@@ -290,7 +296,9 @@ const startStoppableCommand = (
? new ExecFileOutputCancelledError(signal.reason)
: stopped === "timeout" && timeout !== undefined
? timeoutError(command, timeout, processError)
: (outputError ?? processError);
: rootFailed && stopped === undefined
? processError
: (outputError ?? processError);
if (failure !== null && outputError !== undefined)
Reflect.set(failure, "outputTruncated", true);
complete(failure, out, diagnostic);