Closed bug report

WSL_WEDGED flaps declare/clear every 30s and tells the user to restart Windows - the cosmetic workspace_stats 3s cap feeds the wedged detector

kaleymer · 1mo ago ·closed by John Lauer

ah 1.0.164 (build c4613fbf5d69aba7824d5992894d59604f6b31ee-dirty) · ab 2.1.8 · Windows 11 10.0.26200.9168 · WSL 2.7.11.0 (kernel 6.18.33.2-2)

What happened

Machine slept. I woke it, clicked Update on Hydrogen's banner, and the update to 1.0.164 installed and relaunched. Immediately after it came back, the "Starting Workspace…" console filled with the full multi-line WSL_WEDGED declared paragraph, repeating on a metronome — declared / forensics / cleared, over and over, with nothing else in the dialog, for the whole life of the session.

At the same time I was restarting my own personal WSL distros (Ubuntu-24.04, with several VS Code WSL windows reconnecting to it). That turns out to be the whole story, and it is the point of this report: an ordinary, transient, user-caused slowdown of wslservice gets escalated into a repeating "restart Windows" banner.

Verbatim, from %APPDATA%\hydrogen-desktop\hydrogen-desktop.log:

10:22:47.858 [wsl] WSL_WEDGED declared: 3 consecutive wsl.exe timeouts (last: workspace_stats probe (3s)). The Windows WSL management plane (wslservice) is not responding; the distro itself may be fine. Suppressing non-essential wsl.exe calls; workspace health falls back to HTTP probes. A Windows restart (or restarting WslService) clears this.
10:22:48.162 [wsl] WSL_WEDGED forensics: 12 wsl.exe processes system-wide
10:22:50.331 [wsl] WSL_WEDGED cleared: wsl.exe is responding again
10:23:18.070 [wsl] WSL_WEDGED declared: 3 consecutive wsl.exe timeouts (last: workspace_stats probe (3s)). ...
10:23:18.304 [wsl] WSL_WEDGED forensics: 12 wsl.exe processes system-wide
10:23:20.400 [wsl] WSL_WEDGED cleared: wsl.exe is responding again

The shape of it

Declared and cleared alternate on an exact 30-second period — the workspace_stats poll interval — and each declaration is cleared ~2 seconds later:

10:21:47.604 declared → 10:21:50.129 cleared   (2.5s)
10:22:17.724 declared → 10:22:20.249 cleared   (2.5s)
10:22:47.858 declared → 10:22:50.331 cleared   (2.5s)
10:23:18.070 declared → 10:23:20.400 cleared   (2.3s)
10:23:48.236 declared → 10:23:50.482 cleared   (2.2s)
10:24:18.404 declared → 10:24:20.618 cleared   (2.2s)
10:24:48.566 declared → 10:24:50.702 cleared   (2.1s)
10:25:18.767 declared → 10:25:20.769 cleared   (2.0s)

It ran from 10:14:00 until the app exited at 10:25:25 — 11 minutes, 12 declare/clear pairs in that session alone. Across the whole log there are 31 declarations, and 25 of them name workspace_stats probe (3s) as the trigger:

  25  last: workspace_stats probe (3s)
   5  last: wsl ["-d", "Adom-Workspace", "bash", "-lc", "pgrep -f 'code-server' ..."] (60s)
   1  last: wsl ["--status"] (60s)

WSL was never wedged. The 3s-capped stats probe was simply slower than 3s while wslservice was busy with my distro restarts; it tripped the wedged threshold every poll cycle, and the next call with a real budget answered fine and cleared it.

How slow "slow" actually was

Sampled live on the machine while writing this up, wsl.exe --list --verbose — about the cheapest management-plane call there is — timed out at 15 seconds:

Adom-Workspace state:
TIMEOUT

An earlier run of the same command did return, showing Ubuntu-24.04 Running, Adom-Workspace Stopped, docker-desktop Stopped. vmmemWSL was at 5.5 GB.

So a 3-second cap on an in-distro stats read is nowhere near survivable under normal user load. Nothing was broken — wslservice was just busy.

Three defects

1. The workspace_stats 3s cap has two handlers and they disagree. Sixteen minutes earlier in the same log, the identical timeout took the graceful path:

09:58:09.261 [wsl] workspace is healthy (code-server answering); CPU/RAM readout skipped this poll, the in-distro stats read exceeded its 3s cap. Numbers resume when wsl.exe responds.

That is the right behavior — a 3s cap on a cosmetic CPU/RAM readout is a soft budget, not evidence of a management-plane failure. But the same timeout also increments the global consecutive-timeout counter that declares WSL_WEDGED. A best-effort probe should be excluded from the wedged detector entirely, or the detector should only count probes that carry a real (60s) budget.

2. No hysteresis — the state flaps instead of latching or backing off. Declaring and clearing every 30s for 11 minutes is noise, not signal. Something cleared 2s after being declared should never have been declared. Wants a minimum dwell time before declaring, and suppression so a repeat declaration inside N minutes does not re-emit the whole paragraph.

3. The message is a 60-word paragraph that tells the user to restart Windows. Printed 12 times into a startup dialog that had nothing else in it, on a machine where nothing was wrong and the actual cause was the user restarting their own distros. Even with the flapping fixed, this is far too heavy for a console line — a one-line summary with the detail behind a disclosure, and no "restart Windows" advice until the condition has genuinely persisted.

Timeline

10:13:44.978 [wake-heal] detected: wake detected (automatic, slept ~15m33s)
10:13:45.061 [main-watchdog] MAIN THREAD BLOCKED for ~939s ...
10:14:00.484 [wsl] WSL_WEDGED declared: ... (last: workspace_stats probe (3s))
10:14:14.646 Applying staged update 1.0.164 from ...Adom.Hydrogen_1.0.164_x64-setup.exe
10:14:16.496 Spawned relauncher ... exiting so the installer can replace files
=== Hydrogen Desktop started at 2026-09-03 10:14:34 ===
10:15:01.848 [wsl] WSL_WEDGED forensics: 8 wsl.exe processes system-wide
   ... flap continues every 30s ...
10:25:25.325 [app] Exiting pid=70516 after 650s

The flap starts in the pre-update process right after wake, survives the update and relaunch, and continues in the new 1.0.164 process. So it is not a one-off of a wedged post-wake state, and it is not new in 1.0.164 — but 1.0.164 is where it was seen, and the post-update "Starting Workspace…" dialog is what made it visible.

Note on the forensics: N wsl.exe line — it is misleading

I first read the rising count (4 → 8 → 10 → 12) as Hydrogen leaking probe processes. That was wrong, and the line invites the mistake. Checking parentage of every live wsl.exe:

  PID Parent ParentName Cmd
40084  39272 Code       wsl.exe -d Ubuntu-24.04 sh -c "$VSCODE_WSL_EXT_LOCATION/scripts/wslSe...
18468  40084 wsl        wsl.exe -d Ubuntu-24.04 sh -c "$VSCODE_WSL_EXT_LOCATION/scripts/wslSe...
46296  39272 Code       wsl.exe -d Ubuntu-24.04 -e /home/<user>/.vscode-server/bin/110a328ea54b4...
40924  46296 wsl        wsl.exe -d Ubuntu-24.04 -e /home/<user>/.vscode-server/bin/110a328ea54b4...
 ... (10 total, all parent=Code, all -d Ubuntu-24.04) ...
26824  16968 hydrogen   wsl -d Adom-Workspace -u root -- bash -lc "systemctl restart adom-relay.service ...
57216  26824 wsl        wsl -d Adom-Workspace -u root -- bash -lc "systemctl restart adom-relay.service ...

Every accumulating process belongs to VS Code, attached to my personal Ubuntu-24.04 — including a pair 10 days old. Hydrogen's own contribution was a single transient pair against Adom-Workspace, which reaped normally by the next sample.

That is not a Hydrogen bug, but it is a diagnostics bug: forensics: 12 wsl.exe processes system-wide is a system-wide count printed inside a Hydrogen-blames-WSL message, so it reads as Hydrogen's own process leak to anyone triaging it (it fooled me for a while). Suggest either splitting it into own-vs-other (3 spawned by us, 9 by other processes) or naming the top parent process — which would also have put the actual cause, VS Code hammering wslservice, right on the line.

Expected

Starting Workspace… shows workspace startup progress. A slow cosmetic stats read is skipped quietly (as the 09:58 line already does), does not declare a management-plane failure, does not flap every 30 seconds, and does not tell the user to restart Windows because another app was busy with WSL.

Full log excerpt attached.

Possibly related: adom/hydrogen#26 (false-negative health probe forcing code-server restarts while code-server is actually up) — same family of "a probe's timeout is read as a failure of the thing being probed."

wsl-wedged-excerpt.log

5 Replies

kaleymer · 1mo ago

Follow-up: the wedged state was REAL this time, and that makes defect 2 worse — it is the clear that is wrong

Kept digging after filing, because the workspace never came back. Important correction to my own report: on this machine right now, WSL genuinely is wedged. Measured directly, outside Hydrogen:

wsl.exe --list --verbose            -> TIMED OUT at 15s
wsl.exe -d Adom-Workspace -- echo   -> TIMED OUT at 75s (elapsed 80.2s)

So the original report's framing ("WSL was never wedged, it was just a slow cosmetic probe") is only true of the earlier flap. The detector's declaration is now correct. What is still wrong — and is worse than I first described — is the clear.

Wsl/Service/CreateInstance/0x800705b4 (ERROR_TIMEOUT) is the concrete failure, from the log:

10:29:53.595 [wsl] systemd code-server setup failed (wsl ["-d", "Adom-Workspace", "-u", "root", "--", ...])
  exited with Some(-1): This operation returned because the timeout period expired.
  Error code: Wsl/Service/CreateInstance/0x800705b4), using held-child fallback

Why the flap is the real bug, not a cosmetic one

Hydrogen declares WSL_WEDGED, correctly, and says it is "suppressing non-essential wsl.exe calls". Then ~2s later some cheap call returns and it clears. Then 28s later it declares again. Over and over, ~90 times in this log.

The consequence is not just log noise: Hydrogen never actually stays in the degraded mode it just entered. Each clear re-enables the non-essential wsl.exe calls it had just decided to suppress, which it then fires into an already-jammed wslservice. The suppression that is supposed to let the service drain never gets to run for more than two seconds at a stretch.

So defect 2 should probably be re-prioritised: the declare threshold is defensible; the clear needs to be much stricter (require N consecutive successes, or a successful expensive call — not whichever cheap one happens to return), plus a minimum dwell time before clearing.

Recovery through ah's own API cannot get out of this state

Tried the documented recovery path end to end. None of it recovers, and two things actively mislead:

1. /container-restart latches user_stopped and leaves it latched when the start fails.

10:39:09.826 [control] Workspace restart requested via API, emitting restart event
10:39:09.850 [wsl] user stop requested, latching stopped state (suppresses stats/health probes so nothing auto-restarts the distro)
10:39:09.851 [cmd] $ wsl --terminate Adom-Workspace

A restart takes the user-stop latch. The terminate succeeds, the start then fails against the wedged service, and the workspace is left both stopped and latched — with /container-status reporting:

"narrative": "Workspace is stopped by the user. Start it from the Adom menu (or POST /container-start).",
"user_stopped": true

which is false — the user did not stop it, a restart did. Anyone (human or AI) triaging from that line concludes the user stopped their own workspace. Suggest a restart should either not take the user-stop latch at all, or clear it when the start half fails.

2. code-server started as HD-held child is logged while the distro is not running.

10:46:57.732 [wsl] code-server started as HD-held child on 0.0.0.0:7380

At that moment, and continuously after it, health reports:

"code_server_alive_in_distro": false,
"code_server_listening_in_distro": false,
"distro_running": false,
"status": "exited"

The held-child fallback reports success on spawn without confirming the distro came up or the listener bound. That success line is what sends you looking in the wrong place.

Sequence, for reproduction

  1. Machine sleeps ~15 min; on wake [main-watchdog] MAIN THREAD BLOCKED for ~939s.
  2. Concurrently, several VS Code WSL windows reconnect to an unrelated personal distro, saturating wslservice.
  3. wslservice stops servicing CreateInstance within any timeout ah uses.
  4. WSL_WEDGED flaps declare/clear every 30s indefinitely, never holding suppression.
  5. /container-restart → latches user-stopped, terminates, fails to start.
  6. /container-start ×2 → "start requested", ok:true, distro stays exited.
  7. Only remedy left is wsl --shutdown (or restarting WslService), which also tears down the user's unrelated distros and any unsaved work in them.

Step 7 is the part worth designing for: the only exit from this state is a machine-wide WSL teardown that costs the user their other work. If ah can detect CreateInstance timeouts specifically, it could say so plainly and name that cost, rather than repeating a generic "a Windows restart clears this" paragraph every 30 seconds.

Ruled out while diagnosing

For anyone triaging a similar report — these were checked and are not involved:

  • No golden-image migration. No --import / unregister / rootfs / bake activity anywhere in the log; Adom-Workspace is still registered: true, setup state complete, 21/21 open-welcome DONE.
  • Not the 1.0.164 WSLg change. %USERPROFILE%\.wslconfig is untouched since Aug 11 and contains no guiApplications; GET /wsl/gui reports configured: null, enabled: true, so setup never wrote it (setup was already complete, so the step never re-ran on update).
  • Not a stale app. 1.0.164 is current for the public tier.

Environment unchanged from the original report: ah 1.0.164 (c4613fbf-dirty) · ab 2.1.8 · Windows 11 10.0.26200.9168 · WSL 2.7.11.0.

kaleymer · 1mo ago

Resolved on this machine by wsl --shutdown — and the recovery itself is evidence

Ran wsl --shutdown. Two things worth recording.

1. The shutdown itself took 70 seconds. That is a command that normally returns in a second or two, and it is the cleanest single measurement of how jammed wslservice was:

wsl.exe --shutdown    -> completed, no output, elapsed 70.1s

2. Everything recovered immediately afterwards, with no Hydrogen restart needed.

wsl --list --verbose  -> 0.7s   (was: TIMED OUT at 15s)

  NAME              STATE           VERSION
* Ubuntu-24.04      Running         2
  docker-desktop    Stopped         2
  Adom-Workspace    Running         2

Hydrogen picked the workspace back up on its own:

10:55:21 running=True cs_alive=True listening=True host_reachable=True EDITOR_WILL_LOAD=True
10:55:42 running=True cs_alive=True listening=True host_reachable=True EDITOR_WILL_LOAD=True
10:56:02 running=True cs_alive=True listening=True host_reachable=True EDITOR_WILL_LOAD=True
10:56:22 running=True cs_alive=True listening=True host_reachable=True EDITOR_WILL_LOAD=True
10:56:43 running=True cs_alive=True listening=True host_reachable=True EDITOR_WILL_LOAD=True
10:57:03 running=True cs_alive=True listening=True host_reachable=True EDITOR_WILL_LOAD=True

Stable across ~2 minutes. No reinstall, no re-setup, no distro re-import, no ah restart.

What this confirms about the diagnosis

Hydrogen's install, the Adom-Workspace distro, and its contents were never damaged. The entire outage was wslservice refusing CreateInstance, and every symptom above it — the flapping banner, the failed /container-start calls, the held-child fallback, the editor stuck on "Starting AI Environment" — was downstream of that one thing.

That is a good result for ah's recovery path: once WSL is healthy again, ah self-heals with no user action. The gap is entirely in the ~40 minutes before that, where ah had the information to say what was wrong and instead repeated a generic paragraph 90 times.

The concrete ask, now that the shape is confirmed

ah already distinguishes the failure in its own logs — it logged Wsl/Service/CreateInstance/0x800705b4 at 10:29:53. So it can tell "the management plane will not create instances" apart from "a probe was slow". Suggested behavior when CreateInstance times out repeatedly:

  1. Latch the wedged state (per the hysteresis point above) instead of clearing it 2s later, so call-suppression actually holds and stops adding load.
  2. Say the specific thing once, not the generic thing every 30s — something like: "WSL's management plane is not starting distros (CreateInstance timeout). wsl --shutdown fixes this. Note: that restarts all your WSL distros, so save work in other WSL windows first."
  3. Name the cost. The remedy is machine-wide and takes down the user's unrelated distros. The current message says "A Windows restart (or restarting WslService) clears this" without mentioning that either option costs the user their other WSL sessions — and without mentioning wsl --shutdown, which is the cheapest of the three and the one that actually worked here.

Point 3 is the one I'd prioritise for users: the fix was 70 seconds away the whole time, and nothing in the UI pointed at it.

The other two defects from the follow-up stand and are unaffected by the recovery — the false user_stopped after a failed restart, and code-server started as HD-held child logging success while distro_running: false.

John Lauer · 1mo ago

Partial fix shipped in ah 1.0.197 (insiders): the toast half is fixed, the flapping half is not.

What changed. The banner was sticky (duration 0) and its "toast already shown" latch was one-way, so once raised it stayed on screen telling the user to restart Windows even after WSL_WEDGED cleared, which it does by itself on the next successful wsl.exe call. It now retracts on the first healthy stats poll and re-arms for a genuinely new wedge, and the copy leads with "this usually clears itself within a couple of minutes, often after the laptop wakes", with restarting Windows as the fallback rather than the instruction.

What did not change, and is the substance of this issue: the declare/clear flapping itself. wsl_note_timeout declares on the third consecutive timeout and wsl_note_ok clears on the next success, with no hysteresis, so a wslservice that is merely slow (your own distro restarting, VS Code WSL windows reconnecting) still oscillates on a metronome and still writes the full forensic paragraph each time. Your 70-second wsl --shutdown is the right measurement of that: the plane was jammed, not broken, and a transient jam should not read as a fault.

Reasonable next step, not yet done: require the streak to hold for a duration rather than a count, log the declaration once per episode rather than per declaration, and treat a clear inside a short window as "still degraded" rather than recovery.

John Lauer · 1mo ago

Triaged with John today: fix now, as one change with #76. The wedge flag will only trip when a real management call (a start or stop the user or the setup cascade asked for) hangs. The stats read stops using wsl.exe entirely (CPU and GPU come from Win32, RAM and the OOM counter come over HTTP from inside the VM), so it can never feed the detector, and the WSL service banner can never be painted by a slow but healthy VM again.

John Lauer · 1mo ago

Shipped in Hydrogen 1.0.387, as one change with #76.

The flapping came from the cosmetic stats read: the poll spawned wsl.exe every ~10s to read /proc inside the distro, and when that read exceeded its 3s cap three times (easy on a busy or thrashing box) it tripped the WSL_WEDGED detector, which painted the "restart Windows" banner over a workspace that was answering fine over HTTP.

Now the meter reads CPU, RAM and the kernel OOM count from the adom-vscode extension over HTTP (GET /stats, 1.1.59). The stats read no longer touches wsl.exe at all, so it can never feed the wedge detector. WSL_WEDGED is now set only by a genuine start/stop management-plane hang, and cleared as before. Verified on arav-rog: the meter now reports stats_source "distro" over HTTP with real numbers and wsl_wedged false.

Log in to reply.