Worktree background full checkout fails with 'not-a-git-repo' when repo has `submodule.recurse = true`

Resolved 💬 3 comments Opened May 1, 2026 by thomasqbrady Closed May 5, 2026

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 = true in .git/config

Repro

  1. Clone any repo that has at least one submodule.
  2. In its local config, set git config submodule.recurse true (a perfectly valid workflow setting — it just makes pulls/checkouts recurse).
  3. Open the repo in the Claude desktop app and start a new conversation with the worktree toggle enabled.
  4. 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.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗