Skip to content

[Bug]: A failing post-checkout hook fails the worktree launch, hides the reason, and leaves a worktree that blocks retry #21635

Closed as not planned

Description

@pavelzbornik

What happened

A scheduled task launched two threads with t3_thread_launch and workspaceStrategy: {type: "worktree", baseRef: "v2", branch: "chore/deps-20261019", startFromOrigin: true} (and a second one for chore/upstream-sync-20261019). Both threads failed with:

Workspace preparation failed during provision worktree: Git command failed in GitVcsDriver.createWorktree (D:\Github\nanoclaw): git worktree add failed

The worktrees were in fact created. Both paths under ~/.t3/worktrees/nanoclaw/ existed afterwards, checked out at the base commit with a clean status. A retry 48 minutes later then failed with git worktree add failed (branch_already_exists) on the branch the first attempt had left behind.

Cause

The repository has a shared post-checkout hook in .git/hooks. It was installed by the pre-commit framework for an older branch. The launched branch has no .pre-commit-config.yaml, so the hook prints No .pre-commit-config.yaml file was found and exits 1. Git runs post-checkout after git worktree add has finished the checkout and returns the hook's exit status. So git worktree add exits 1 with a complete worktree on disk.

createWorktree treats any non-zero exit as failure (fallbackErrorDetail: "git worktree add failed"). It does not call onWorktreeClaimed and does not remove the worktree or branch. Later launches or retries with the same branch name fail on the leftovers.

Reproduced by hand, outside T3:

$ git worktree add -b tmp/hook-repro C:/tmp/hook-repro-wt origin/v2
Preparing worktree (new branch 'tmp/hook-repro')
HEAD is now at f7d3a205 ...
No .pre-commit-config.yaml file was found
- To temporarily silence this, run `PRE_COMMIT_ALLOW_NO_CONFIG=1 git ...`
$ echo $?
1
$ git worktree list | grep hook-repro
C:/tmp/hook-repro-wt  f7d3a205 [tmp/hook-repro]

Three separate problems

  1. A failing post-checkout hook fails the whole launch, even though git has already produced a usable worktree. feat: Surface post-checkout hook output during worktree creation #551 proposed surfacing hook output and turning hook failures into a warning, and was closed.
  2. The error drops git's stderr. executeGit records only stderrLength, so the thread shows "git worktree add failed" with no hint that a hook printed the reason. I had to rerun the command by hand to find it. Git command errors discard stderr, making failures opaque to callers #4380 asks for the same in general.
  3. The failed launch leaves the worktree and branch behind, which blocks retry. Parallel worktree creation hits the fixed 5-minute git timeout and leaves orphan worktrees that block retry #15541 reports this for timeouts and [Bug]: Worktree creation fails at configureBaseRef when .git/config is locked and leaves an orphan worktree + branch #11735 for a locked .git/config. A failing hook is a third way in, and probably the easiest to hit, since any repo with a strict post-checkout hook triggers it every time.

Possible fixes:

  • When git worktree add exits non-zero, check whether the worktree exists at the requested path and is registered in git worktree list. If it is, keep it and attach a warning with the stderr. If it isn't, remove whatever was created.
  • Or run the add with -c core.hooksPath=<empty dir> and run post-checkout separately as a warning step. That changes behaviour for users who rely on the hook, so the first option is probably safer.
  • Include stderr (trimmed) in GitCommandError.detail.

Steps to reproduce

  1. In any repo, add .git/hooks/post-checkout containing #!/bin/sh followed by exit 1.
  2. Create a thread in "new worktree" mode on that project.
  3. The thread fails with "git worktree add failed". git worktree list shows the worktree and its branch.
  4. Retry with the same branch name. It fails with branch_already_exists.

Version

0.1.1-nightly.20261017.3238 (desktop, Windows)

Environment

Windows 11 Pro 10.0.22631, git 2.50.1.windows.1, Claude Code provider. Repo on D:, worktrees under C:\Users\<user>\.t3\worktrees.

Evidence

# server.trace.ndjson (start time UTC, span, durationMs, exit)
07:00:57 GitVcsDriver.createWorktree        4524 Success   (allowNonZeroExit inside the span)
07:00:57 createWorktree                     4656 Failure   git worktree add failed
07:00:51 ThreadLaunchService.prepareInBackground 10255 Failure  failed during provision-worktree
07:48:51 createWorktree                      197 Failure   git worktree add failed (branch_already_exists)

Workaround

Add --skip-on-missing-config to the ARGS=(hook-impl ...) line of the pre-commit-generated post-checkout hook, or install the hook with pre-commit install --allow-missing-config. Then remove the leftover worktree and branch by hand.

Filed by

Claude Code (claude-opus-5-5), on behalf of @pavelzbornik

Activity

  1. juliusmarminge commented on Oct 19, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Closing as a duplicate of #15541. In that issue a timed-out git worktree add also left a finished worktree and branch that blocked retry. A failing post-checkout hook is a new way into the same state. Any non-zero exit from git worktree add fails the launch (GitVcsDriverCore.ts:3503-3525). The claim and cleanup step, onWorktreeClaimed, only runs after a successful add (GitVcsDriverCore.ts:3527-3529), so nothing removes what git already created.

    Leaving stderr out is deliberate. Git's stderr can echo arguments and remote URLs that carry credentials, so it never leaves the driver. Known failures get a reason tag instead, such as branch_already_exists (GitVcsDriverCore.ts:452-461, 1120-1131).

    The hook failing is a repo setup issue. Installing it with pre-commit install --allow-missing-config, then removing the leftover worktree and branch, avoids it, as you found.

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

    duplicateThis issue or pull request already existsvia-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