Skip to content

Thread resumes in a worktree folder without .git instead of recreating the checkout #24390

Open

Description

@szemroda

What happened

Earlier storage-cleanup removals on Windows left the worktree folder in place with only node_modules (sometimes .next) inside, with no .git and no tracked files. When a thread pointing at such a folder runs again, T3 Code sees that the folder exists and skips checkout recovery, so the agent works in a directory with no checkout.

Expected: a managed worktree path that is not a valid checkout is recovered (cleared and recreated from the thread's branch), the same way a missing folder is.

Diagnosis

  • On this machine, 75 folders under ~\.t3\worktrees\<project>\ were in this state. Most were removed in bursts on 2026-10-16 and 2026-10-21 on 0.1.1. Their node_modules are pnpm layouts full of NTFS junctions. With Git for Windows ≥ 2.54, git worktree remove skips junctions and leaves them and their parent folders behind, which is exactly what apps/server/src/vcs/worktreeRemoval.ts describes. fix(server): removing a worktree on Windows no longer deletes what its junctions point to #21099 fixed the removal for new cleanups, but nothing repairs folders that are already broken.
  • apps/server/src/orchestration-v2/ProviderTurnStartService.ts:476-480 (v0.1.2; same on main at ~491-493) recreates the worktree only when !fileSystem.exists(worktreePath). A folder holding only node_modules passes the existence check, so pruneWorktrees + createWorktree never run.
  • The same folders also make every storage-cleanup sweep log a failure. storageCleanup.ts:803 calls fs.stat(path.join(worktreePath, ".git")), which throws ENOENT instead of returning keep("not a linked worktree"). In the retained traces, the same thread failed this way at 10:47 and 11:48 (hourly).

Suggested direction: in ProviderTurnStartService, treat a managed worktree path without a valid .git link (or one not registered in git worktree list) like a missing one. In storageCleanup.ts, handle a missing .git as a "kept" outcome, not an error.

Steps to reproduce

Deterministic, without depending on Git's junction behaviour:

  1. Create a worktree thread in a pnpm project and let setup install node_modules.
  2. Stop the thread, then delete everything in the worktree folder except node_modules (including the .git file). Optionally run git worktree prune in the main repo.
  3. Send a new message to the thread.
  4. Expected: "provider turn start recreating missing worktree" and a fresh checkout of the thread's branch. Actual (from source): the existence check passes, recovery is skipped, and the turn runs in the gutted folder.
  5. Separately, the next storage-cleanup sweep logs "storage cleanup failed for worktree" with NotFound … stat …\.git.

Natural repro on 0.1.1: Windows, Git for Windows ≥ 2.54, pnpm project, and any worktree cleanup rule removing the worktree.

Version

0.1.2 (recovery gap and sweep error; also on main). The leftover folders themselves were created on 0.1.1.

Environment

Windows 11 (10.0.26200) x64, Node v26.12.1, Git 2.55.0.windows.3, pnpm projects, desktop app with local server

Evidence

# leftover folder (one of 75)
~\.t3\worktrees\zesty-explorer\t3code-df890ade\   contents: node_modules\   (no .git; mtime 2026-10-16 11:35)
node_modules\clsx  Junction -> ...\t3code-df890ade\node_modules\.pnpm\clsx@2.1.1\node_modules\clsx

# server.trace.ndjson, StorageCleanup.cleanWorktrees, 2026-10-24 10:47 and 11:48
"storage cleanup failed for worktree", threadId 6ba3d897-8d26-4c66-b75a-17d412d03866
  reason: { _tag: "NotFound", module: "FileSystem", method: "stat",
            pathOrDescriptor: "~\\.t3\\worktrees\\zesty-explorer\\t3code-df890ade\\.git" }

Related issues

#21374 covers a missing worktree directory where the re-create fails with branch_checked_out_in_worktree; this report is the case where the directory still exists, so re-create never starts. #19124 is about turns failing after merge removes the worktree and branch. #21099 (PR) fixed how the folders get left behind, not recovery from them.

Fix applied or workaround

With the user's approval, deleted the 75 folders whose only top-level entries were node_modules/.next and that had no .git. Deletion used Node's fs.rmSync, which unlinks junctions without following them; no junction pointed outside its own folder. Threads using those paths should now go through the normal missing-worktree recreation. Folders with any other content were left alone.

Filed by

Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage

Activity

  1. phillip-hirsch commented on Oct 26, 2026

    @phillip-hirsch

    Note

    🤖 Claude Opus 5.5 responding on behalf of @phillip-hirsch

    The same cleanup failure happened on this Windows machine from a different cause. An agent cleaned up its subagents' worktrees by running git worktree remove itself. Git removed the checkouts and unregistered the worktrees, but two folders failed to delete with error: failed to delete '…\t3-0c653d44': Permission denied. The subagents probably still had them open. Both folders were left empty, with no node_modules. Every storage cleanup run after that failed with NotFound: FileSystem.stat (…\t3-0c653d44\.git). I deleted the empty folders by hand. With #24398, cleanup would keep them as "not a linked worktree" instead of failing.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions