Closed bug report

After self-update, two adom-vscode extension hosts answer /health; the selector flaps and the /exec fallback reports the wrong port

kaleymer · 1mo ago ·closed by John Lauer

Following the 1.0.431 self-update, Hydrogen logs this every ~35 seconds, indefinitely:

[adom-vscode] 2 hosts answer /health ([8821, 8822]); using the newest, port 8821

Observed continuously from 13:51 to at least 14:15 on 2026-09-17. Related to #85 (same update), but independent of it.

Why there are two hosts

On launch Hydrogen deliberately leaves the existing code-server alone:

13:51:05.341 [wsl] code-server already active + answering, leaving it alone (no restart, no editor reload)
13:51:05.341 [wsl] code-server managed by systemd, no HD-held child

That is reasonable on its own, but it means the pre-update extension host survives while the new build brings up its own. Both answer /health, and the selector disambiguates with "use the newest" — a heuristic, not an identity check.

The selector flaps

Nothing actually moved; the selector just keeps changing its mind:

13:51:06  [ai-tabs] mirror connected to adom-vscode /tabs/events on port 8822
13:51:24  [ai-tabs] mirror connected to adom-vscode /tabs/events on port 8822
13:51:40  [adom-vscode] 2 hosts answer /health ([8821, 8822]); using the newest, port 8821
13:51:40  [ai-tabs] mirror: the extension moved off port 8822; reconnecting
13:51:43  [ai-tabs] mirror connected to adom-vscode /tabs/events on port 8821
13:51:43  [wake-heal] reload: editor reloaded: the adom-vscode extension host restarted (attached again on port 8821)

"The extension moved off port 8822" and "the extension host restarted" are both misreadings of the situation — both hosts were up the whole time.

The reporting bug

The announced port and the dialed URL disagree:

13:51:08.640 [exec] :8821 /exec unusable (adom-vscode /exec transport:
             error sending request for url (http://127.0.0.1:8822/exec)), falling back to wsl.exe
13:51:09.656 [exec] :8821 /exec unusable (… url (http://127.0.0.1:8822/exec)), falling back to wsl.exe

It reports :8821 as unusable while the HTTP client's own error names http://127.0.0.1:8822/exec. Two possible readings, and the log alone cannot distinguish them:

  • Cosmetic: the message is formatted from the selected port while the request used a candidate port. The log then misattributes every failure, which is what made this take a while to unpick.
  • Functional: the request targeted the wrong host and a healthy one got marked dead. Hydrogen then degrades to the slower wsl.exe path unnecessarily.

Either way the identity cross-check gives up:

13:51:11.610 [wsl-startup] :8821 not answering: iframe likely stale, reloading to reconnect
13:51:11.940 [wsl-startup] distro-identity cross-check skipped (:8821 /exec unavailable — ext <1.0.16 or editor not up yet)

Note the suggested causes in that last line ("ext <1.0.16 or editor not up yet") are both wrong here — the editor was up and answering.

Suggested fixes

  1. Log the port actually dialed, taken from the same value used to build the URL.
  2. Select the host by identity (instance id, build stamp, or the existing distro-identity value) rather than by "newest", so a stale host can be recognised and retired instead of competed with.
  3. When a self-update leaves a previous extension host alive, shut it down or hand off explicitly rather than leaving both to race. If keeping both is intentional, the health line should say which one belongs to the current build instead of reporting a tie every 35 seconds.

Environment

  • Hydrogen 1.0.431, insiders tier, build efa297192b87a81f251d488fd5363289c4c5e52b-dirty
  • Windows 10.0.26200.9457
  • WSL 2.7.11.0, networkingMode=mirrored, hostAddressLoopback=true
  • code_server 7380, proxy 47083, control 47084, cdp 47085 (reclaimed from drift at 63478/63479/63480 on this launch)

2 Replies

John Lauer · 1mo ago

Agreed on all three, and thank you for the trace. This is next after 1.0.432 (the #85 fix, building now).

What I'll change:

  1. The /exec log line names the port the request actually dialed, taken from the same value that built the URL. Right now the message is formatted from the selected port while the request used a candidate, so every failure is misattributed; your "cosmetic" reading is the right one, but it cost you time and it costs the identity cross-check its verdict.
  2. Host selection by identity, not "newest": each host's /health already reports startedAt and the extension version; Hydrogen will pick the host whose page load it owns (the one the current VS Code pane started) and retire the other by asking it to detach, instead of competing with it every 35 s.
  3. After a self-update, the pane reload the VS Code client does on its own leaves the old extension host alive for minutes. Hydrogen will tell the old host to stand down once the new one answers, so there is one host, and the health line goes quiet.

I'll post the version here when it ships.

John Lauer · 1mo ago

Shipped: Hydrogen 1.0.435 (insiders) with adom-vscode 1.1.92 in the workspace.

What changed:

  1. Identity, not age. The workbench page is the one party that knows which extension host belongs to its own load; adom-vscode's title-bar script now records it (window.__adomVscodeHost, the host started after the page load). When more than one host answers /health, Hydrogen reads that over CDP and takes the page's host. "Newest" is only the fallback until the page has named its host.
  2. The stale host is retired. Every other healthy host gets POST /standdown (new in 1.1.92): it marks itself detached, drops its port file and closes its server, so it is not found again. Asked once per host, and only on the page's word, never on a guess.
  3. One line per change, with the facts. The multi-host line prints when the set or the choice changes, with each host's start time, not every 35 s: 2 hosts answer /health (8821 started …; 8823 started …); using 8821 (the workbench page's own host).
  4. The /exec fallback names the port it dialed. Your reading was right: the message was formatted from the selected port while the request used a candidate. The distro-identity cross-check also dials the live host's port now instead of a hardcoded 8821, and its skip message says what happened ("/exec on port N did not answer") instead of guessing at causes.
  5. The mirror's line reads "the live extension host is on port X now (this stream is from port Y); reconnecting", which is what is happening.

Verified on a laptop: with the real host on 8821 and a second host on 8823 answering /health with a newer start time (which the old rule would have picked), Hydrogen chose 8821 as the page's own host, logged the line once, and sent /standdown to 8823, which answered 200. A forced pane reload replaced the host cleanly with no second host left behind.

Not reproduced here: the exact 25-minute survival of the old host after a self-update. Hydrogen's own reload retires the old host within seconds on this machine. If you see two hosts again on 1.0.435 or later, the log line above will show which one the page claimed and whether the other was asked to stand down; please paste it.

Closing as fixed.

Log in to reply.