You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Remote launcher writes $ instead of its pid into the runtime install lock #15696
Start a second launch while the first is still downloading the archive (a reconnect does this on a slow link).
The cause in one line:
"A @@X@@ B".replaceAll("@@X@@",'printf "$$"')// 'A printf "$" B'
Expected behavior
The install lock records the installing launcher's pid, so a second launch waits for it, as the comments around the lock describe.
Actual behavior
The lock's pid file holds a literal $. kill -0 '$' fails, so every waiting launch treats the owner as dead, deletes the lock and starts its own download into a second staging directory.
applyScriptPlaceholders inserts values with replaceAll(token, value). A string replacement treats $$ as an escaped $, so the runner's "$$" becomes "$" when the runner is inserted into the launch and pairing scripts.
tunnel.test.ts checks buildRemoteT3RunnerScript output before that insertion, so it passes.
Suggested fix: result.replaceAll(`@@${token}@@`, () => value), plus a test that the launch script contains "$$" > "$T3_LOCK/pid.tmp". A function replacer also keeps $&, $` and $' in any inserted script literal.
Impact
Minor bug or occasional failure
Version or commit
v0.0.45 (observed on the remote); same code on main at 4ee6bfd
Environment
Desktop app on macOS, SSH remote on Linux arm64
Written by an agent (Claude Code, Claude Opus 5.5)
Thanks for the precise report, @altaywtf. The one-line replaceAll repro made this quick to confirm.
What I found
Confirmed on main at 4ee6bfd.
applyScriptPlaceholders in packages/ssh/src/tunnel.ts inserts values with String.prototype.replaceAll. In a string replacement, $$ becomes $, so the runner's printf '%s\n' "$$" > "$T3_LOCK/pid.tmp" line is written into ~/.t3/ssh-launch/<id>/run-t3.sh as "$". The launch and pairing scripts both embed the runner through @@T3_RUNNER_SCRIPT@@.
The launch script's own run-t3.next.$$ is unaffected, because that $$ is in the template rather than in a replacement value.
kill -0 '$' always fails, so a second launch during a slow download treats the lock owner as dead and starts another install. A single launch still installs fine.
tunnel.test.ts only checks the runner before it's inserted, so the existing test passes.
Likely fix area
There's already an open PR for this, #14598 by @Gigioxx. It switches applyScriptPlaceholders to a function replacer and adds a test that the launch script embeds the runner unchanged. It's mergeable and its checks are green, so it matches what you suggested.
Fixed by #14598, which just landed on main: applyScriptPlaceholders now uses a replacer function, so the runner's "$$" reaches the launch script intact and the install lock records the real PID. It also adds a byte-for-byte test that the launch script embeds the runner. Closing — thanks for the precise report!
Activity
juliusmarminge commentedon Oct 4, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks for the precise report, @altaywtf. The one-line
replaceAllrepro made this quick to confirm.What I found
Confirmed on
mainat4ee6bfd.applyScriptPlaceholdersinpackages/ssh/src/tunnel.tsinserts values withString.prototype.replaceAll. In a string replacement,$$becomes$, so the runner'sprintf '%s\n' "$$" > "$T3_LOCK/pid.tmp"line is written into~/.t3/ssh-launch/<id>/run-t3.shas"$". The launch and pairing scripts both embed the runner through@@T3_RUNNER_SCRIPT@@.run-t3.next.$$is unaffected, because that$$is in the template rather than in a replacement value.kill -0 '$'always fails, so a second launch during a slow download treats the lock owner as dead and starts another install. A single launch still installs fine.tunnel.test.tsonly checks the runner before it's inserted, so the existing test passes.Likely fix area
There's already an open PR for this, #14598 by @Gigioxx. It switches
applyScriptPlaceholdersto a function replacer and adds a test that the launch script embeds the runner unchanged. It's mergeable and its checks are green, so it matches what you suggested.A maintainer will decide on the fix direction.
fix(ssh): use function replacer for script placeholders (pingdotgg#15696
juliusmarminge commentedon Oct 11, 2026
Note
Grok responding on behalf of Julius.
Fixed by #14598, which just landed on main:
applyScriptPlaceholdersnow uses a replacer function, so the runner's"$$"reaches the launch script intact and the install lock records the real PID. It also adds a byte-for-byte test that the launch script embeds the runner. Closing — thanks for the precise report!