Version: sfw-free 1.15.0
Summary
bug: When sfw hits an internal error, it logs the error, tears down its proxy, and exits 0. The wrapped command is killed part-way through, but the caller sees success.
expected: Instead, when an internal error happens, I would expect exit 1 or similar.
In my case, I'm using sfw to install dependencies in a script that has other actions afterward, and this misreported command success causes downstream problems in my script.
Potentially the same root cause as #37, opposite symptom — there the package manager hangs forever, here it exits clean. Both would follow from a handler that cleans up without signalling failure.
Detail: root cause (suspected)
From the bundled dist/sfw-free.js in the released binary (confirmed by strings):
var Gv = u(e => {
console.error("Socket Firewall encountered an unexpected error:", e?.stack ?? e),
yl() // runCleanup — tears down the proxy servers
}, "uncaughtErrorHandler");
process.on("uncaughtException", Gv);
process.on("unhandledRejection", Gv);
var e2 = process.exit.bind(process);
process.exit = (e => { yl(), e2(e) }); // exit() is wrapped, but the handler never calls it
The handler logs, runs cleanup, and stops. It never rethrows, never calls process.exit(1), and never sets process.exitCode.
The sting is in the registration itself. Since Node 15 an unhandled rejection is fatal by default, but registering an unhandledRejection listener suppresses that default. So the handler turns a would-be exit-1 into a no-op: the error is logged, cleanup closes the proxy, nothing sets a failure code, the event loop drains, and the process exits 0.
If that is right, it is not specific to fetch failed — any internal error reaching this handler would produce a silent success. The same handler is in v1.13.1 and v1.14.0 (identical apart from minified names), so it would be long-standing rather than a recent regression.
To be clear about what I verified: I read the handler out of the shipped binary, and I confirmed in isolation that a handler of this shape exits 0 — a script that registers unhandledRejection and sets no exit code exits 0 on Node, where the same script without the handler exits 1. I did not instrument sfw itself to rule out something else setting the code.
Expected behavior
An internal error should produce a non-zero exit code, so the caller can tell the command did not complete:
var Gv = u(e => {
console.error("Socket Firewall encountered an unexpected error:", e?.stack ?? e),
process.exitCode = 1,
yl()
}, "uncaughtErrorHandler");
process.exitCode rather than process.exit(1) lets cleanup finish and any running child be reaped normally.
Secondary, if useful: kill the wrapped process when the proxy goes down so it fails fast, and word the message so it reads as an abort rather than a warning.
Evidence
I cannot trigger the internal fetch failure on demand — it depends on a transient failure of sfw's own outbound call. I have two occurrences on CI runners, hours apart, on an unchanged 1.15.0.
From one CI job running sfw pnpm install --frozen-lockfile:
17:21:01.319 <workspace> postinstall$ pnpm run-codegen
17:21:01.677 Socket Firewall encountered an unexpected error: TypeError: fetch failed
17:21:01.683 at node:internal/deps/undici/undici:15845:13
17:21:01.683 at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
17:21:01.683 at async /home/runner/work/firewall/firewall/dist/sfw-free.js:42:59277
17:21:01.845 [install step ends — reported SUCCESS]
sfw died mid-postinstall, and the step ended 168 ms later with exit 0.
The same crash site appears in #9 (comment) — identical offset sfw-free.js:42:59277, on the macOS build via sfw npm install. Same code path.
Evidence from my job that the install did not finish: pnpm prints <pkg> postinstall: Done for every lifecycle script that completes. Every dependency postinstall in that log has its Done line. The ones still running when sfw died do not, and the expected generated files were absent from disk afterwards. (The job failed steps later due to missing files.)
Related issues
Version: sfw-free 1.15.0
Summary
bug: When sfw hits an internal error, it logs the error, tears down its proxy, and exits 0. The wrapped command is killed part-way through, but the caller sees success.
expected: Instead, when an internal error happens, I would expect exit 1 or similar.
In my case, I'm using sfw to install dependencies in a script that has other actions afterward, and this misreported command success causes downstream problems in my script.
Potentially the same root cause as #37, opposite symptom — there the package manager hangs forever, here it exits clean. Both would follow from a handler that cleans up without signalling failure.
Detail: root cause (suspected)
From the bundled
dist/sfw-free.jsin the released binary (confirmed bystrings):The handler logs, runs cleanup, and stops. It never rethrows, never calls
process.exit(1), and never setsprocess.exitCode.The sting is in the registration itself. Since Node 15 an unhandled rejection is fatal by default, but registering an
unhandledRejectionlistener suppresses that default. So the handler turns a would-be exit-1 into a no-op: the error is logged, cleanup closes the proxy, nothing sets a failure code, the event loop drains, and the process exits 0.If that is right, it is not specific to
fetch failed— any internal error reaching this handler would produce a silent success. The same handler is in v1.13.1 and v1.14.0 (identical apart from minified names), so it would be long-standing rather than a recent regression.To be clear about what I verified: I read the handler out of the shipped binary, and I confirmed in isolation that a handler of this shape exits 0 — a script that registers
unhandledRejectionand sets no exit code exits 0 on Node, where the same script without the handler exits 1. I did not instrument sfw itself to rule out something else setting the code.Expected behavior
An internal error should produce a non-zero exit code, so the caller can tell the command did not complete:
process.exitCoderather thanprocess.exit(1)lets cleanup finish and any running child be reaped normally.Secondary, if useful: kill the wrapped process when the proxy goes down so it fails fast, and word the message so it reads as an abort rather than a warning.
Evidence
I cannot trigger the internal
fetchfailure on demand — it depends on a transient failure of sfw's own outbound call. I have two occurrences on CI runners, hours apart, on an unchanged 1.15.0.From one CI job running
sfw pnpm install --frozen-lockfile:sfw died mid-postinstall, and the step ended 168 ms later with exit 0.
The same crash site appears in #9 (comment) — identical offset
sfw-free.js:42:59277, on the macOS build viasfw npm install. Same code path.Evidence from my job that the install did not finish: pnpm prints
<pkg> postinstall: Donefor every lifecycle script that completes. Every dependency postinstall in that log has itsDoneline. The ones still running when sfw died do not, and the expected generated files were absent from disk afterwards. (The job failed steps later due to missing files.)Related issues
TypeError: fetch failedreports. Those treat it as a connectivity problem. This is about what sfw does after the error, not the error itself.