[BUG] Worktree/chip spawn fails in git-submodule repos: 'Background full checkout failed: other' (extensions.worktreeConfig arms inherited core.worktree)
Summary
Starting a worktree session (or clicking a background-task chip) in a project that is itself a git submodule fails with "Background full checkout failed: other". Every git command inside the freshly created worktree dies with:
fatal: cannot chdir to '../../../../../../path/to/super/Projects/<submodule>': No such file or directory
after which the app removes the orphaned worktree and the session never starts.
Environment
- Claude Desktop (macOS, Darwin 25.5), Claude Code binary 2.1.197
- git 2.x (reproduced on the stock Apple git and Homebrew git)
- Project root = an absorbed submodule of a super-repo (its
.gitis a gitfile pointing at<super>/.git/modules/<sub>; the module's--localconfig carries the standard relativecore.worktree)
Mechanism (established by controlled experiment, not inference)
The lethal state requires both of these on the submodule's shared (--local) config:
| state | --local core.worktree | extensions.worktreeConfig | in-worktree git |
|---|---|---|---|
| (a) raw absorbed submodule | set | off | works (relative path resolved from the common gitdir) |
| (b) armed | set | true | breaks — fatal: cannot chdir (exit 128) |
| (c) healed | unset (moved to main worktree's config.worktree) | true | works |
With extensions.worktreeConfig=true, git resolves the shared relative core.worktree from each linked worktree's own gitdir (.git/modules/<sub>/worktrees/<name> — two levels deeper than the module gitdir the path was written for) → nonexistent path → every command fails. This is the documented git requirement: enabling worktreeConfig obliges relocating core.worktree into the main worktree's config.worktree (git-worktree(1) CONFIGURATION FILE section). The app enables the extension but never relocates core.worktree.
Why the app arms its own landmine (one-success-then-break)
From ~/Library/Logs/Claude/main.log, the worktree setup runs git config extensions.worktreeConfig true (and git config --worktree core.longpaths true) after checkout:
- First spawn on a raw submodule (state a): succeeds — and its setup writes
extensions.worktreeConfig=trueinto the submodule's shared config → repo is now state (b). - Every subsequent spawn fails — the first in-worktree command (
git ls-tree --name-only HEAD -- CLAUDE.md ...) dies withcannot chdir, and the recovery commands (git config extensions.worktreeConfig true,git config --worktree core.longpaths true) fail with the same error because they also run with cwd inside the broken worktree (chicken-and-egg).
Observed exactly as a 12:46 success followed by a 13:49 failure in the same repo, and reproduced deterministically.
Additional trap: even after a manual heal (state c), any git submodule update --init re-adds --local core.worktree (git re-writes it for absorbed submodules) → repo returns to state (b) and chips break again. So a one-shot fix on the app side must be re-checked at every worktree spawn.
Minimal repro
# super repo with one absorbed submodule "sub"
git init super && cd super
git submodule add <any-repo-url> sub
git commit -m "add sub"
cd sub
git worktree add .claude/worktrees/t1 HEAD --detach
git -C .claude/worktrees/t1 status # works (state a)
git worktree remove --force .claude/worktrees/t1
git config extensions.worktreeConfig true # what the app's worktree setup does
git worktree add .claude/worktrees/t2 HEAD --detach
git -C .claude/worktrees/t2 status # fatal: cannot chdir ... (state b)
Suggested fix
In the worktree-creation path, when the base repo's --local config contains core.worktree (i.e. the base is an absorbed submodule), run against the base repo, before git worktree add (not from inside the new worktree):
cw=$(git config --local core.worktree) # non-empty → absorbed submodule
git config extensions.worktreeConfig true
git config --worktree core.worktree "$cw" # relocate to the MAIN worktree's config.worktree
git config --local --unset core.worktree
and re-check on every spawn (because git submodule update re-adds the --local value). Verified: this takes the repo to state (c), where both the main checkout and all linked worktrees work.
Related
Adjacent submodule/worktree family, different failure signatures: #54904 (not-a-git-repo, repo containing submodules), #55387, #60131, #27156.