Skip to content

[BUG] Internal errors exit 0: uncaughtErrorHandler should instead set failure code #61

Description

@nmassey

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions