Repository navigation
Worktree bootstrap fetch hangs for the full 30s timeout when a GUI SSH agent is waiting on key authorization #11930
Description
What happened
I've noticed the last few days that the mobile app fails to actually start a thread on my tailscale machines. It only happens when I am trying to start the thread with a new worktree. The remote machine will eventually get the thread/message, but it will not start the agent work. I just created a new thread without a worktree and it worked fine.
The error shown on mobile:
COULD NOT START TASK Git command failed in GitVcsDriver.fetchRemote: Git command timed out. Your prompt was kept in the project draft.
Diagnosis
Starting a thread with New worktree + Start from origin runs fetchRemote in ws.ts, which calls GitVcsDriver.fetchRemote with no timeoutMs and so inherits DEFAULT_TIMEOUT_MS = 30_000.
When origin is an SSH remote and the user's agent is a GUI agent that requires authorization for the requesting application (1Password, Secretive, Yubikey touch), the agent raises an approval prompt. When the request comes from a background app, that prompt is a passive tray notification rather than a focused window. ssh blocks on the agent socket, git makes no progress, and the driver kills it at 30s reporting Git command timed out.
STATUS_UPSTREAM_REFRESH_ENV sets GCM_INTERACTIVE=never, GIT_ASKPASS="", GIT_TERMINAL_PROMPT=0, SSH_ASKPASS="" and SSH_ASKPASS_REQUIRE=never specifically to stop auth from hanging, and GitVcsDriverCore.test.ts asserts all five reach git. The guard is incomplete: those variables govern terminal and askpass prompting only, and have no effect on an agent prompting over SSH_AUTH_SOCK. The test stubs GIT_SSH with a wrapper that exit 1s immediately, so it verifies the variables are set but never exercises an agent that blocks.
Note that IdentityAgent in ~/.ssh/config overrides SSH_AUTH_SOCK, so the agent is reached regardless of what environment the server process inherits. This is not an environment-propagation problem.
Three things make this hard to self-diagnose:
- The error says
Git command timed out, pointing at the network or the repo, when the real cause is a pending key authorization. - The 30s deadline expires before the agent's own prompt deadline (60s for 1Password), so a user who notices the tray icon still has under 30s to approve.
- It presents as intermittent. It fails when nobody is at the machine to approve, which is exactly when the mobile app is used.
This is orthogonal to repo size. The repo here is 18 refs, 3.5M .git, 1 pack, and fetches in ~1.1s once authorized.
Note for #10943: timeoutMs: null converts the 30s timeout into a stall lasting until the agent's own prompt timeout, after which git fails with a genuine auth error. Better message, worse latency. A targeted fix would detect an outstanding agent handshake with no fetch progress and surface an authorization-specific error, rather than tuning the deadline.
Steps to reproduce
- Configure a GUI SSH agent that requires per-application approval, and point
IdentityAgentat it in~/.ssh/config. - Give a project an SSH
origin(git@github.com:<user>/<repo>.git). - Ensure the agent has no standing authorization for the requesting process, and leave the machine so no prompt is approved.
- From the mobile app, start a thread on that project with New worktree and Start from origin.
- After 30s the thread fails with
Git command failed in GitVcsDriver.fetchRemote: Git command timed out.No thread row is persisted and the prompt returns to the project draft.
A thread on the same project without a worktree succeeds, because no network fetch occurs.
Version
0.0.41-nightly.20260914.1707 (desktop Nightly app). Contains #11633.
Environment
macOS 27.0.0 arm64, Node v24.18.1, desktop Nightly app as server, mobile app as originating surface, 1Password 8 as SSH agent.
Evidence
T3 Code trace spans (UTC):
ws.rpc.orchestration.dispatchCommand 30101ms Failure
fetchRemote 30004ms Failure
GitVcsDriver.fetchRemote 30004ms Failure git.args_count=3
runGitCommand 30001ms Interrupted
runGitCommand started 15:38:19.061, killed 15:38:49.061
1Password agent log, same window (UTC):
15:38:19.548 op-ssh-agent: Notifying user through tray icon that they
have a background prompt waiting
15:39:19.400 op-ssh-agent: ssh authorization prompt timed out
Second occurrence, same pattern:
16:14:51 background prompt waiting
16:15:50 ssh authorization prompt timed out
Direct reproduction outside the app, agent authorization left unanswered,
running git under the driver's exact environment:
git ls-remote git@github.com:<user>/<repo>.git HEAD
-> 60.86s, then:
sign_and_send_pubkey: signing failed for ED25519 ".../id_ed25519"
from agent: communication with agent failed
Agent log: ssh authorization prompt timed out
The runGitCommand child span confirms git was spawned and ran the full
budget before Effect interrupted it. The same fetch completes in ~1.1s
once the agent request is authorized.Related issues
#10916 is the same symptom caused by a genuinely slow fetch on a large repo; this fails on a 3.5M repo whose fetch takes ~1.1s, so the cause is distinct. #10179 is the inverse shape, SSH_AUTH_SOCK missing entirely under OrbStack systemd, where the agent is unreachable rather than prompting. #1190 (closed) covers repeated unlock prompts in the UI, not a blocked fetch.
Fix applied or workaround
Moving the project's origin to HTTPS with the gh auth git-credential helper removes the SSH agent from the path. Verified on the affected machine, running git under the driver's exact environment with GIT_SSH_COMMAND=/bin/false and SSH_AUTH_SOCK unset so no SSH fallback is possible:
public repo, git fetch --quiet origin -> exit 0, 0.49s
private repo, git ls-remote HEAD -> ref returned, 0.66s
SSH control, same environment -> 60.86s, agent failure
The credential helper authenticates correctly despite GIT_ASKPASS="" and GIT_TERMINAL_PROMPT=0. Disabling per-request approval for the SSH key is the alternative if SSH remotes must be kept.
Filed by
claude (opus-5) via t3 triage
Activity
juliusmarminge commentedon Sep 15, 2026
Thanks for the tight repro and the 1Password /
git ls-remotetimings. This checks out on currentmain(7235701de) and is still distinct from #10916.What’s going wrong
New-thread New worktree + Start from origin in
apps/server/src/ws.tsalways runsgitWorkflow.fetchRemotebeforecreateWorktree:GitVcsDriver.fetchRemotethen runsgit fetch --quiet <remote>(scoped to the selected branch after #11633) withSTATUS_UPSTREAM_REFRESH_ENVand notimeoutMsoverride:execute()still appliesDEFAULT_TIMEOUT_MS = 30_000whentimeoutMsis omitted, then surfacesGit command timed out.That matches the traces (fetchRemote30004ms /runGitCommandkilled at exactly 30s) and the mobile card (COULD NOT START TASK+ draft restore).Those five env vars only suppress terminal / askpass prompting. They do not touch an agent speaking over
SSH_AUTH_SOCK. Git inheritsprocess.env(so the socket is present), andIdentityAgentin~/.ssh/configreaches the agent even if the server process never saw that socket. The child sits on the agent handshake; git makes no progress; Effect interrupts at 30s.GitVcsDriverCore.test.ts(makes background upstream status fetches non-interactive) asserts the five variables are set, but theGIT_SSHwrapperexit 1s immediately. It never exercises an agent that blocks.This is why it looks intermittent: it fails when nobody is at the machine to approve the tray prompt, which is exactly the mobile / tailnet case. The 30s knife also fires before 1Password’s own 60s prompt deadline, so a user who does notice the icon still has a short window.
Repo size is a red herring here. 18 refs / 3.5M
.git/ one pack, and the same fetch is ~1.1s once authorized. Directgit ls-remoteunder the driver’s env, left unanswered, ran 60.86s and thencommunication with agent failed— the agent’s timeout, not a transfer timeout.Why this isn’t a duplicate of #10916
Same symptom string, different cause:
ls-remote1.61s)tmp_pack_*from mid-download kills#11633 already scoped the bootstrap fetch. That helps #10916’s 10k-tag case. It does nothing for an SSH handshake that never starts the transfer.
Related work that does not close this
timeoutMs: nullonfetchRemote) is the right fix for [Bug]: New worktree creation fails when git fetch exceeds the hardcoded 30s timeout #10916 and must stayFixes #10916only. Do not attributeCloses #11930. Lifting the deadline turns a 30s false timeout into a stall until the agent’s own prompt timeout (60s here), then a real auth error. Better message, worse latency for the mobile case.SSH_AUTH_SOCKmissing under OrbStack systemd / login-shell hydration, so the agent is unreachable (Permission denied (publickey)), not prompting. If fix(server): recover the login shell SSH agent #10312 lands, more service-launched servers will have a socket — and GUI agents on that socket will start hitting this hang instead of a fast publickey failure.fetchRemoteForStatus, 5s budget, now configurable / disableable). [codex] add configurable automatic git fetch interval #2605 addedSSH_ASKPASS_REQUIRE=neverand the fetch-interval setting. That closed the askpass UI loop. It does not cover per-app agent authorization overSSH_AUTH_SOCK, and it is not the start-from-origin bootstrap path.Suggested fix
Do not retune the deadline. Detect an outstanding agent handshake with no fetch progress and fail with an authorization-specific error (and keep the prompt in the project draft). A short “no git/ssh progress” window on an SSH remote is enough to distinguish this from a slow pack. The current
Git command timed out.string points at the network/repo and is why this is hard to self-diagnose.A timeout-only change, including #10943, is the wrong tool for this issue.
Workaround
Move
originto HTTPS and usegh auth git-credential. The reporter verified fetch/ls-remote under the driver’s env (withGIT_SSH_COMMAND=/bin/falseandSSH_AUTH_SOCKunset so SSH cannot sneak back). Alternatively, grant the T3 server process standing approval for the key so the agent does not prompt per request.Triage: confirmed bug on
apps/server. Not a duplicate. Accepting for a server fix. #10943 must not close this.