EnterWorktree (path=) rejects a valid linked worktree under separate-git-dir (external git dir)

Status Open
Reported on v2.1.206
Maintainer reply None cached
Activity 1 comment · opened Jul 22, 2026

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=keep returns a no-op (“no active EnterWorktree session”), and retrying path= 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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗