Worktree background full checkout fails with 'not-a-git-repo' when repo has `submodule.recurse = true`
Summary
When creating a Claude Code worktree against a repo that has submodule.recurse = true set in its local git config, the staged background full checkout always fails with Background full checkout failed: not-a-git-repo. The worktree directory ends up partially populated (only the selective paths — CLAUDE.md, .claude, .mcp.json — are checked out) and the new conversation cannot be entered.
I reproduced this against the Claude desktop app and tracked it down to the stageCheckout implementation in the bundled JS.
Environment
- Claude desktop app: 1.5354.0
- Claude Code CLI: 2.1.126
- macOS: 26.3.1
- git: 2.50.1 (Apple Git-155)
- Repo has 2 git submodules and
submodule.recurse = truein.git/config
Repro
- Clone any repo that has at least one submodule.
- In its local config, set
git config submodule.recurse true(a perfectly valid workflow setting — it just makes pulls/checkouts recurse). - Open the repo in the Claude desktop app and start a new conversation with the worktree toggle enabled.
- The background pre-creation surfaces a red banner:
Something went wrong … Background full checkout failed: not-a-git-repo.
Expected
Worktree creation succeeds. Submodules in the new worktree don't have to be initialized — the staged checkout simply shouldn't try to recurse into them.
Actual
The promise returned by stageCheckout rejects. Running the same command Claude Code runs, by hand, in the freshly-created worktree:
$ git -c core.longpaths=true checkout HEAD -- . ':(exclude).claude'
fatal: not a git repository: ../../../../../.git/worktrees/<name>/modules/packages/<submodule>
fatal: could not reset submodule index
That stderr matches the not a git repository branch in the error classifier, which is then surfaced as not-a-git-repo in the banner.
Adding -c submodule.recurse=false to the same command makes it succeed (exit 0):
$ git -c submodule.recurse=false -c core.longpaths=true checkout HEAD -- . ':(exclude).claude'
$ echo $?
0
Root cause
In the bundled app.asar/.vite/build/index.js, stageCheckout invokes:
this.execGit(
[...t, "-c", "core.longpaths=true", "checkout", "HEAD", "--", ".", ":(exclude).claude"],
A,
{ extraEnv: { LC_ALL: "C" } }
)
t is just the LFS filter overrides; nothing is passed to suppress submodule recursion. With submodule.recurse = true in the parent repo's config, this git checkout recurses into submodules — but in a brand-new worktree the submodules' gitlinks (.git/worktrees/<name>/modules/<path>) have never been created, so git aborts with not a git repository.
The same call site likely needs the fix in both the selective and full-checkout invocations.
Suggested fix
Pass -c submodule.recurse=false (or equivalently --no-recurse-submodules where the subcommand supports it) to the staged checkout commands in stageCheckout. Worktrees never have initialized submodules at staging time, so recursion is never useful here — only harmful.
A minimal patch would be inserting "-c", "submodule.recurse=false" into the args array right alongside the existing -c core.longpaths=true. Same treatment is probably warranted for the selective checkout a few lines above.
Workaround
For anyone hitting this: move submodule.recurse = true out of shared .git/config and into the main worktree's per-worktree config:
git config --unset submodule.recurse
git config --worktree submodule.recurse true # extensions.worktreeConfig must be true
Then new worktrees won't inherit the setting and Claude Code's staged checkout will succeed, while the main worktree continues to recurse on pulls/checkouts as before.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗