Skip to content

Report a clear error and exit cleanly when the HTTP server can't bind its port#469

Merged
alistair3149 merged 1 commit into
masterfrom
listen-error-handler
Jul 23, 2026
Merged

Report a clear error and exit cleanly when the HTTP server can't bind its port#469
alistair3149 merged 1 commit into
masterfrom
listen-error-handler

Conversation

@alistair3149

Copy link
Copy Markdown
Member

Summary

app.listen emits its 'error' event asynchronously — after startHttpServer() has already returned — so index.ts's main().catch never sees it. With no 'error' listener, a bind failure (EADDRINUSE, EACCES) surfaced as an uncaught exception with a raw stack trace.

This attaches an error handler so a bind failure produces a clear, actionable message and a clean exit(1):

Cannot start HTTP server: 127.0.0.1:3000 is already in use.

What changed

  • New handleListenError(err, host, port, onFatal?) in streamableHttp.ts: logs EADDRINUSE / EACCES / generic distinctly and terminates. onFatal is injectable so it is unit-testable without exiting the test process. Wired via httpServer.on('error', …) in startHttpServer().
  • Guard the success log: Express fires the app.listen callback even when the bind failed (httpServer.listening is false then), which would otherwise print a misleading listening on … line right before the failure. A bind failure now logs only the clear error.

This is the deferred follow-up from the boot-extraction (#466), now unblocked since startHttpServer() is on master.

Testing

  • Test-driven unit tests for handleListenError (EADDRINUSE / EACCES / generic → correct message + onFatal(1)).
  • Real two-servers-on-one-port smoke: the second process exits 1, logs only Cannot start HTTP server: … is already in use., and prints no stack trace and no misleading listening line; a successfully-bound server still logs listening.
  • Full gate (fmt:check, lint, tsc, tests) green.

🤖 Generated with Claude Code

… its port

app.listen emits its 'error' event asynchronously, after startHttpServer() has
already returned, so index.ts's main().catch never sees it. With no 'error'
listener, a net.Server error (EADDRINUSE, EACCES) surfaces as an uncaught
exception with a raw stack trace and a nonzero-but-noisy exit.

Attach an error handler that logs an actionable message ("… is already in use",
"permission denied binding …", or the raw message for anything else) and exits 1.
The handler is extracted as handleListenError with an injectable onFatal so it is
unit-testable without exiting the test process.

Also guard the success log: Express fires the app.listen callback even when the
bind failed (httpServer.listening is false then), which would otherwise print a
misleading "listening on …" line right before the failure. A bind failure now
produces only the clear error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alistair3149
alistair3149 marked this pull request as ready for review July 23, 2026 05:13
@alistair3149
alistair3149 merged commit a481fb3 into master Jul 23, 2026
1 check passed
@alistair3149
alistair3149 deleted the listen-error-handler branch July 23, 2026 05:13
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