Skip to content

Fix flaky remote-client tests: surface admin server bind errors and avoid ephemeral ports - #214

Merged
pimterry merged 2 commits into
httptoolkit:mainfrom
philipp-winterle:fix/admin-server-bind-errors
Sep 22, 2026
Merged

pimterry merged 2 commits into
httptoolkit:mainfrom
philipp-winterle:fix/admin-server-bind-errors

Conversation

@philipp-winterle

Copy link
Copy Markdown
Contributor

The CI run for #213 (run 35230056297) failed with 30 failures, all of them Failed to connect to admin server at http://127.0.0.1:45454 (plus Server is not running from the teardown hooks), starting at plugins.spec.ts. The full node suite passes locally on the same code, and main on the same base was green.

What the log shows

Every admin server start from plugins.spec.ts onwards failed to bind its port, but start() still resolved. Express 5 passes listen errors to the app.listen() callback, and AdminServer.start() resolved from that callback unconditionally, so a failed bind looked like a running server. Clients then got ECONNREFUSED, and stop() threw Server is not running.

Likely cause (inferred, not proven)

45454 is inside the default Linux ephemeral port range (32768-60999). An outgoing connection from another test can take it as its local port, and then the admin server can't bind it. That fits a one-off failure that later passes, but I can't prove it from the log.

Changes

  1. Reject admin server start when the port can't be bound: start() now rejects with the bind error (e.g. EADDRINUSE) and leaves the server stopped, so stop() works and a later start() on another port succeeds. Covered by three new tests that fail without the fix.
  2. Run test admin servers on port 30454: that port is outside the default ephemeral ranges on Linux, macOS and Windows. Only the node tests that start their own admin server change; the default-port test and the browser tests (with-admin) are untouched.

Even if the cause is something else, (1) means the next failure of this kind reports one clear EADDRINUSE instead of 30 unrelated-looking connection errors.

Testing

  • Full node suite locally, twice in a row: 833 passing, 0 failing (Node 24).
  • Browser tests not run locally.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SzQcMX9TVncn4dz6PhGuWn

Express 5 passes listen errors to the listen callback, so a failed bind
(e.g. EADDRINUSE) resolved start() as if the server were running. Later
stop() then failed with 'Server is not running', and every client got
ECONNREFUSED. Now start() rejects with the bind error and leaves the
admin server stopped, so it can be stopped or started again.
The node tests started every admin server on the default port 45454,
which is inside the default Linux ephemeral port range (32768-60999).
An outgoing connection from another test can take that port, so the
admin server fails to bind and every remote-client test after it
fails with ECONNREFUSED. Tests now use port 30454, which isn't in the
default ephemeral range on Linux, macOS or Windows.
@pimterry
pimterry merged commit 4850ca0 into httptoolkit:main Sep 22, 2026
10 checks passed
@pimterry

Copy link
Copy Markdown
Member

Thanks @philipp-winterle, merged. Looks like this came in in the recent upgrade to Express v5, good catch 👍

I appreciate the engagement with the project, but just as feedback: opening a blast of 5 AI generated PRs like this as an opening move is a bit much. If possible please open issues before PRs for debatable cases (not bug fixes) like httptoolkit/httptoolkit-server#246 and httptoolkit/httptoolkit-ui#271 so they can be discussed, and I'd recommend in general not opening loads of PRs at once in projects like this.

I'd also really strongly recommend writing the text yourself in PR descriptions. The massive wall of text here is clearly straight from an LLM, it has very little information in a lot of words, which is just outsourcing the work of extracting the actual meaning there (5 times) to maintainers, instead of writing a clear description yourself.

An example to illustrate:

The admin server listen() call didn't check for errors, so failures like EADDRINUSE were silently swallowed. The server also uses a port in the Linux ephemeral range by default, which could easily be in use with outgoing traffic, triggering that failure. The combination is my best guess for the flaky test failure in #213, and good to fix regardless.

I've updated the server to properly catch errors and clean up, and updated the tests to use a non-ephemeral port.

Anyway, regardless of that, thank you for contributing! In case you're not aware HTTP Toolkit Pro is free for all contributors to all https://github.com/httptoolkit repos. I've just set up an account with a year free linked to your git commit email, just click 'Get Pro' then 'Log into existing account' to get started.

@philipp-winterle

Copy link
Copy Markdown
Contributor Author

You are absolutely right. Thanks for the feedback.

I stumbled over the issue and fixed it and already reviewed it. But I was too lazy to setup the pr so I gave the job to the AI. But I do not like the ai text as well. Will write the desc on my own next time. Sry.

Thanks for the pro <3

@philipp-winterle
philipp-winterle deleted the fix/admin-server-bind-errors branch September 22, 2026 19:26
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.

2 participants