Closed bug report

Dock launch: /workspace/exec expands $var and $(...) on the host before the container runs the step

John Lauer · 1mo ago ·closed by John Lauer

Measured on ConfRoomROG (Hydrogen v1.0.201, 2026-09-05) through hd_api POST /workspace/exec, the route the dock falls back to when the editor extension has no /exec on :8821. Commands with shell substitutions are expanded on the host side before the container sees them: (1) 'for i in 1 2; do echo n$i; done' prints 'n\nn' ($i already empty); (2) 'echo $(seq 1 3)' fails with 'bash: line 2: 2: command not found' (the substitution ran on the host and its lines were executed); (3) the Fusion dock step sh -c 'PATH="$HOME/.local/bin:$PATH"; for i in $(seq 1 15); do ...; done; exec fusion-dashboard serve --print-url' fails in 0.3s with 'sh: 2: Syntax error: word unexpected (expecting do)', exitCode null. The dock's launch page then shows the step with '✗ exit=? (no output)' while its elapsed timer keeps counting past the step budget (649s seen), so the failure reads as a hang. Expected: the command string reaches the container shell verbatim (no host-side expansion), and a step that returns ok:false stops the timer and shows the stderr. Workaround on our side: the Fusion dashboard's dockbar.json step no longer uses $ or $(...) (shipping in fusion-bridge 1.9.363). Same symptom as the laptop's Hard Refresh Open of the Fusion dashboard on 2026-09-04.

4 Replies

John Lauer · 1mo ago

Root cause confirmed from the code, and it is one boundary, not the dock: every in-distro exec in Hydrogen spawns wsl.exe -d Adom-Workspace -u adom -- bash -lc <command>. With --, wsl.exe re-joins the arguments, wraps the command in double quotes and hands that string to the distro's DEFAULT shell, which performs one round of expansion ($var, $(...), backticks) before bash -lc ever runs. That is exactly your three cases: $i is empty at the outer level (so n, n), $(seq 1 3) expands to three lines and the outer shell runs line 2 as a command, and the Fusion step's $(seq 1 15) inside sh -c '...' becomes a multi-line word where do is not where sh expects it. The setup cascade's own scripts survive this by luck: $HOME expands to the same value one level early.

Fix, on the split-modules branch (commit 7e89758), shipping with the next Hydrogen release once that branch merges today:

  1. /workspace/exec and the other execs in the control crates use --exec instead of --, so argv reaches bash -lc verbatim. The setup cascade's execs in hydrogen-app keep -- for now (they run during first install and get the same change after a virgin-VM cascade).
  2. The dock's /workspace/exec fallback dropped stderr, which is why the console said (no output) for a syntax error the server had actually returned. It now passes through.
  3. A failed step never told the loader, so the elapsed counter kept climbing past the step budget and read as a hang. The store now posts a terminal failure; the loader freezes the counter (stopped after Ns), shows the reason and opens the console.

I will reply here with the three cases re-run through hd_api POST /workspace/exec on a box running the fixed build before closing. Your dockbar.json workaround stays valid either way.

John Lauer · 1mo ago

Proof of the boundary, measured on AdomLapper just now with direct wsl.exe -d Adom-Workspace -u adom <sep> bash -lc <case> calls, nothing of Hydrogen in between:

case -- (today) --exec (the fix)
for i in 1 2; do echo n$i; done n, n n1, n2
echo $(seq 1 3) 1, then bash: line 2: 2: command not found, line 3: 3: command not found 1 2 3
sh -c 'for i in $(seq 1 3); do echo s$i; done' sh: 2: Syntax error: word unexpected (expecting "do") s1, s2, s3

Your three symptoms reproduce byte for byte with --, and vanish with --exec. So the fix is the one-token change at the spawn site, and the release carrying it is what closes this.

John Lauer · 1mo ago

Verified on the real route. Hydrogen 1.0.206 (insiders, published 2026-09-05) on AdomLapper, each case through hd_api POST /workspace/exec:

case 1.0.206
for i in 1 2; do echo n$i; done ok, exit 0, n1 n2
echo $(seq 1 3) ok, exit 0, 1 2 3
sh -c 'for i in $(seq 1 3); do echo s$i; done' ok, exit 0, s1 s2 s3

The dock loader changes (stderr passthrough, the frozen counter with the reason on a failed step) ship in the same build. ConfRoomROG picks 1.0.206 up through the insiders manifest on its next update check. Closing; reopen if a dock step still shows the old behaviour after the update.

John Lauer · 1mo ago

Follow-up, shipped in Hydrogen 1.0.208 (insiders, 2026-09-05), after John pointed out that the whole point of the container route was to stop using wsl.exe: /workspace/exec never tried that route, and the dock's frontend tried the extension itself on a hardcoded :8821 while ConfRoomROG's extension sits on :8822 (execAllowed:true). Every launch step there fell to wsl.exe, which is where the quoting bug lived.

1.0.208:

  • wsl_exec_adom, the helper every in-container command in the control crates goes through (44 call sites), now tries the adom-vscode /exec route first and falls to wsl.exe (wsl_exec_adom_direct, --exec, behind the global lock) only when that route is not up. One choke point, no per-site edits.
  • The dock frontend has no exec router of its own any more: it posts to /workspace/exec and ah picks the route, reads the real port, and reports route: container|wsl in the response. The launch console says so when it had to use wsl.exe.

Verified on AdomLapper, 1.0.208, through hd_api POST /workspace/exec:

case result
for i in 1 2; do echo n$i; done route container, exit 0, n1 n2, 16 ms
echo $(seq 1 3) route container, exit 0, 1 2 3, 10 ms
false route container, exit 1 (the real code, not ?)
three parallel sleep 2 calls all three on the container route, 2.0 s each, 2.8 s wall: concurrent, no serial queue

The wsl.exe fallback remains for the editor-not-up window and keeps the --exec fix from 1.0.206.

Log in to reply.