Skip to content

fix: exit with the real error when the backend cannot bind its port (supersedes #14) - #31

Closed
abubear wants to merge 1 commit into
visualbruno:mainfrom
abubear:ao/3dgenstudio-6/server-listen-error
Closed

fix: exit with the real error when the backend cannot bind its port (supersedes #14)#31
abubear wants to merge 1 commit into
visualbruno:mainfrom
abubear:ao/3dgenstudio-6/server-listen-error

Conversation

@abubear

@abubear abubear commented Sep 1, 2026

Copy link
Copy Markdown

Supersedes #14 by @srbot2016, which no longer merges: main has since gained a
server.on('error') handler of its own (209154b), so the PR conflicts and its
const server = app.listen(...) change is already there. The part of #14 that
still matters is carried here, with credit to the original author on the commit.

The bug that is still on main

server.on('error', (err) => {
  if (err.code !== 'EADDRINUSE') throw err;
  ...
});

A throw inside an 'error' listener becomes an uncaught exception, and
process.on('uncaughtException') at the top of server.js deliberately keeps
this process alive. So any bind failure other than a port collision — @srbot2016
hit listen EACCES: permission denied 0.0.0.0:3001 — leaves a backend process
running that never started listening. The Electron shell waits, times out, and
reports:

Backend startup failed: Backend did not start in time

The real Node.js error is printed only as "Uncaught exception (server kept
alive)" noise, and the process lingers instead of failing.

Reproduced on main (Windows, port inside a WinNAT-reserved range): the process
was still alive 60s later, having never bound.

The change

Every listen failure is fatal and named, then process.exit(1):

  • EADDRINUSE — unchanged message and advice.
  • EACCES — new: says permission denied, points at privileged ports (<1024) and
    at Windows reserved ranges (netsh int ipv4 show excludedportrange protocol=tcp).
  • anything else — prints the error object itself, so the cause is in the log
    rather than behind a timeout.

Nothing is swallowed: the unknown branch logs the full error, and the exit code
now tells the shell, a container, or CI that the start failed.

Testing

npm test does not exist in this repo (no test script or runner), so this was
verified by running the backend:

case how result
normal start PORT=3201 npm start banner prints, server stays up
EADDRINUSE squat the port, then start on it message + advice, exit code 1
EACCES PORT=49726 (WinNAT-reserved range) permission-denied message + advice, exit code 1
EACCES on main (before) same logged as an uncaught exception, process alive after 60s, never listening

npx eslint server.js — clean. Repo-wide npm run lint still fails on
pre-existing react-hooks issues under src/, untouched by this PR.

Note for the maintainer

#14 can be closed once this merges — the fix and the credit both live here.

The listen-error handler rethrew everything that was not EADDRINUSE. A throw
inside an 'error' listener becomes an uncaught exception, and the handler at the
top of server.js deliberately keeps the process alive through those -- so a bind
failure such as EACCES left a backend running that had never started listening.
The desktop shell then reported only "Backend did not start in time", with the
actual Node.js error nowhere in sight.

Every listen failure is now fatal and named: EADDRINUSE and EACCES get an
explanation and a way out, anything else prints the error itself, and all three
exit(1) so the shell, a container, or CI sees a failed start.

Reported by @srbot2016 in #14, which this supersedes.

Co-Authored-By: srbot2016 <38800635+srbot2016@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant