EnterWorktree (path=) rejects a valid linked worktree under separate-git-dir (external git dir)
Summary
EnterWorktree with path= rejects a worktree that git worktree list clearly reports as a linked worktree of the current repo, when the repo uses separate-git-dir (the working tree's .git is a file pointing to an external git directory). It errors:
Cannot enter worktree: <checkout>/.claude/worktrees/feat/x is not a linked worktree of <checkout>.
…even though git itself lists that worktree under the current repo.
Environment
- Claude Code: 2.1.206
- git: 2.39.5 (Apple Git-154)
- macOS: 15.7.4 (arm64)
Root cause (hypothesis, consistent with all evidence)
Under separate-git-dir, git worktree list records the primary/main worktree's path as the external git-dir location, not the actual checkout directory. The EnterWorktree ownership check appears to identify “the current repository” by that primary-worktree path and require the launch cwd to equal it. Under separate-git-dir the two are legitimately different, so the check fails for the real checkout dir, and every linked worktree is judged “not owned by the current repo”.
A membership check against git worktree list --porcelain (the target path is in it), or comparing via git rev-parse --git-common-dir, would handle this correctly.
Minimal repro
# 1. Repo whose .git lives OUTSIDE the working tree (separate-git-dir)
mkdir -p /tmp/co /tmp/gd
cd /tmp/co
git init --separate-git-dir=/tmp/gd .
git commit --allow-empty -m init
# 2. Add a linked worktree (mirrors Claude Code's .claude/worktrees layout)
git worktree add .claude/worktrees/feat/x -b feat/x
# 3. Sanity — git DOES consider it a linked worktree of this repo:
git worktree list --porcelain
# lists: worktree /tmp/co/.claude/worktrees/feat/x (branch feat/x)
# BUT the FIRST (primary) entry is: worktree /tmp/gd (the git dir, NOT /tmp/co)
git rev-parse --show-toplevel # -> /tmp/co (the real checkout)
# 4. Launch Claude Code with cwd = /tmp/co, then call:
# EnterWorktree path="/tmp/co/.claude/worktrees/feat/x"
Expected
EnterWorktree enters the worktree — git already recognizes it as a linked worktree of the current repo (git worktree list --porcelain includes it).
Actual
Cannot enter worktree: /tmp/co/.claude/worktrees/feat/x is not a linked worktree of /tmp/co.
Ruled out
- Not a stale worktree marker.
ExitWorktree action=keepreturns a no-op (“no active EnterWorktree session”), and retryingpath=fails identically. EnterWorktree name=…is not a workaround — the WorktreeCreate hook date-stamps the branch and would create a new worktree instead of entering the existing one (worse if the existing branch is a non-date-stamped name).
Workaround
Launch Claude Code with cwd already inside the worktree directory — then no EnterWorktree call is needed and the ownership pre-check is never hit. Per-command cd into the worktree also works.
separate-git-dir with an external git directory is uncommon but valid — here it keeps the working tree in a cloud-synced folder while the .git data lives outside it.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗