Skip to content

Worktree bootstrap fetch hangs for the full 30s timeout when a GUI SSH agent is waiting on key authorization #11930

Description

@mchisolm0

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:

  1. The error says Git command timed out, pointing at the network or the repo, when the real cause is a pending key authorization.
  2. 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.
  3. 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

  1. Configure a GUI SSH agent that requires per-application approval, and point IdentityAgent at it in ~/.ssh/config.
  2. Give a project an SSH origin (git@github.com:<user>/<repo>.git).
  3. Ensure the agent has no standing authorization for the requesting process, and leave the machine so no prompt is approved.
  4. From the mobile app, start a thread on that project with New worktree and Start from origin.
  5. 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

  1. juliusmarminge commented on Sep 15, 2026

    @juliusmarminge
    Member

    Thanks for the tight repro and the 1Password / git ls-remote timings. This checks out on current main (7235701de) and is still distinct from #10916.

    What’s going wrong

    New-thread New worktree + Start from origin in apps/server/src/ws.ts always runs gitWorkflow.fetchRemote before createWorktree:

    yield* gitWorkflow.fetchRemote({
      cwd: prepareWorktree.projectCwd,
      remoteName: "origin",
      refName: prepareWorktree.baseBranch,
    });

    GitVcsDriver.fetchRemote then runs git fetch --quiet <remote> (scoped to the selected branch after #11633) with STATUS_UPSTREAM_REFRESH_ENV and no timeoutMs override:

    const STATUS_UPSTREAM_REFRESH_ENV = Object.freeze({
      GCM_INTERACTIVE: "never",
      GIT_ASKPASS: "",
      GIT_TERMINAL_PROMPT: "0",
      SSH_ASKPASS: "",
      SSH_ASKPASS_REQUIRE: "never",
    });
    
    // …
    const options = {
      env: STATUS_UPSTREAM_REFRESH_ENV,
      fallbackErrorDetail: `git fetch ${input.remoteName} failed`,
    };

    execute() still applies DEFAULT_TIMEOUT_MS = 30_000 when timeoutMs is omitted, then surfaces Git command timed out. That matches the traces (fetchRemote 30004ms / runGitCommand killed 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 inherits process.env (so the socket is present), and IdentityAgent in ~/.ssh/config reaches 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 the GIT_SSH wrapper exit 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. Direct git ls-remote under the driver’s env, left unanswered, ran 60.86s and then communication with agent failed — the agent’s timeout, not a transfer timeout.

    Why this isn’t a duplicate of #10916

    Same symptom string, different cause:

    #10916 this issue
    Remote HTTPS SSH + GUI agent (1Password)
    What git is doing actually transferring a large/stale remote blocked on key authorization; no pack progress
    Auth healthy (ls-remote 1.61s) agent prompt waiting, then agent timeout
    Repo ~1503 heads / ~10494 tags; unattended fetch 64.90s 18 refs, 3.5M; authorized fetch ~1.1s
    Leftovers tmp_pack_* from mid-download kills none reported

    #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

    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 origin to HTTPS and use gh auth git-credential. The reporter verified fetch/ls-remote under the driver’s env (with GIT_SSH_COMMAND=/bin/false and SSH_AUTH_SOCK unset 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.

  2. added
    bugSomething is broken or behaving incorrectly.
    acceptedfeature request accepted
    via-triageFiled through npx t3 triage
    on Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions