Marketplace clone recurses into git submodules, so marketplaces with large submodules can never refresh

Status Open
Reported on v2.1.292
Maintainer reply None cached
Activity 0 comments · opened Oct 7, 2026

Marketplace clone recurses into git submodules, so marketplaces with large submodules can never refresh

Summary

Claude Code clones a marketplace repository with submodule recursion. For any marketplace whose repository carries large submodules, that clone cannot complete: it transfers far more data than a plugin install needs, exceeds the clone timeout, and on Windows without long-path support fails outright at the 260-character MAX_PATH limit.

The affected marketplace here is chrome-devtools-plugins (ChromeDevTools/chrome-devtools-mcp). Its plugin payload is about 100 KB. The clone attempts to pull a submodule chain whose upstream repositories are 1.6 GB and 4.4 GB, and the submodules are never used by the plugin at runtime.

The consequence is that the marketplace checkout can never be refreshed. Drift between the local checkout and the remote makes the staleness probe request a re-clone, and every re-clone fails — so the checkout stays stale permanently. claude plugin update then reports "already at the latest version" while printing a warning the user has no way to act on.

Environment

| | |
|---|---|
| Claude Code | 2.1.292 |
| OS | Windows 11 Pro, build 26300 |
| git | 2.55.0.windows.5 |
| core.longpaths | unset (defaults to false) |
| HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled | 0 |
| Marketplace | chrome-devtools-plugins → https://github.com/ChromeDevTools/chrome-devtools-mcp.git |

What I measured

A plain clone of the repository is not the problem — it is small and fast:

$ time git clone --depth=1 https://github.com/ChromeDevTools/chrome-devtools-mcp.git plain
real    0m3.1s
$ du -sh plain
18M     plain

The same clone with submodule recursion does not complete. It transferred more than 1.5 GB and was still running after 150 seconds, at which point it failed:

warning: unable to access 'clang/test/Driver/Inputs/mips_fsf_tree/mips-mti-linux-gnu/include/c++/4.9.0/mips-mti-linux-gnu/micromips/el/nan2008/bits/.gitattributes': Filename too long
fatal: cannot create directory at 'clang/test/Driver/Inputs/mips_fsf_tree/mips-mti-linux-gnu/include/c++/4.9.0/mips-mti-linux-gnu/micromips/el/nan2008/bits': Filename too long
fatal: Unable to checkout '3c51ea3619e488db19cd26840ed46d58cfc7062f' in submodule path 'third_party/devtools-frontend/extensions/cxx_debugging/third_party/llvm/src'
fatal: Failed to recurse into submodule path 'third_party/devtools-frontend'

A longer time budget does not rescue this configuration. The failure above happens at checkout, not during transfer, so it is independent of how much time the clone is given: on this machine the clone timeout is reached first, and MAX_PATH is the wall immediately behind it. (core.longpaths would move that wall, but the transfer it would then have to complete is the 1.5 GB+ one described below.)

Through the CLI (wrapped here for readability; the last line is truncated by Claude Code itself):

$ claude plugin update chrome-devtools-mcp@chrome-devtools-plugins -y
Checking for updates for plugin "chrome-devtools-mcp@chrome-devtools-plugins"…
✔ chrome-devtools-mcp is already at the latest version (1.5.0). Warning: marketplace not
  refreshed (Failed to refresh marketplace 'chrome-devtools-plugins': Fetching the marketplace
  from GitHub failed on both attempts. HTTPS
  (https://github.com/ChromeDevTools/chrome-devtools-mcp.git): Failed to clon…)

The installed version was stale at the time, and the refresh that would have corrected it is the operation that fails. claude plugin marketplace update chrome-devtools-plugins additionally hangs indefinitely when stdin is not a TTY — unlike claude plugin update, that subcommand has no -y.

How I established that the marketplace clone is the recursive one

1. The checkout carries a fingerprint that only a recursive clone writes. git clone --recurse-submodules writes a [submodule] active = . section into .git/config; a plain clone of the same repository does not.

$ git clone --depth=1 <url> plain
$ grep -c '^\[submodule\]' plain/.git/config
0

$ git clone --depth=1 --recurse-submodules --shallow-submodules <url> rec
$ grep -A1 '^\[submodule\]' rec/.git/config
[submodule]
        active = .

The live marketplace checkout has exactly that section:

$ cat ~/.claude/plugins/marketplaces/chrome-devtools-plugins/.git/config
[core]
        ...
[submodule]
        active = .
[remote "origin"]
        url = https://github.com/ChromeDevTools/chrome-devtools-mcp.git

It is also a full checkout — .git/config has no partial-clone (promisor) marker, and docs/, scripts/, third_party/ are all present — which rules out the sparse-checkout path.

2. Consistent with the code path in the shipped binary. The clone arguments are chosen conditionally on whether sparse paths were supplied:

let W = [..._He, ...S, "clone", "--depth", "1"];
if (h) {                                   // h = sparse paths supplied
  if (etr(e)) W.push("--filter=blob:none");
  W.push("--no-checkout");
} else {                                   // marketplace clone takes this branch
  W.push("--recurse-submodules", "--shallow-submodules");
}

If this reading is right, the fix may be small: a --no-recurse-submodules variant already exists in the binary and is used for the sparse path — it just is not used for marketplaces.

3. Independently reported. ChromeDevTools/chrome-devtools-mcp#2829 describes the same submodule clone; there it surfaced as a clone timeout (Git clone timed out after 120s, with the same third_party/devtools-frontend registration in the output). That report is the source of the 120 s default figure — I could not confirm the default value from my own environment, only that the clone had not completed after 150 s.

Why the submodules are unnecessary for a plugin install

The plugin's runtime payload is:

.claude-plugin/plugin.json     2 KB
plugin.json                    1 KB
mcp.json                       1 KB
skills/                      100 KB   (7 skills, 14 files)
                            --------
                              ~104 KB
  • mcp.json launches the server as npx --prefix ${PLUGIN_DATA} chrome-devtools-mcp@<version> — it is fetched from npm, not built from this checkout.
  • Searching the plugin's own files for the submodule finds nothing:

``console
$ grep -rl 'third_party\|devtools-frontend' skills/ mcp.json plugin.json .claude-plugin/
(no matches)
``

  • In the marketplace checkout, third_party/devtools-frontend is an empty directory — git created the placeholder and never populated it.

For scale, the submodule chain is:

| repo | GitHub-reported size |
|---|---|
| ChromeDevTools/devtools-frontend | 1.6 GB |
| llvm/llvm-project (nested at extensions/cxx_debugging/third_party/llvm/src) | 4.4 GB |

These are build and test dependencies of the upstream project. A marketplace checkout exists to read .claude-plugin/marketplace.json and the plugins' manifests and skill directories. It never needs to build the upstream project.

Suggested fix

Use --no-recurse-submodules for marketplace clones. Concretely, the non-sparse branch above could push --no-recurse-submodules, or marketplaces could take the sparse path as well.

If submodule support must be retained for plugins that legitimately ship skills inside a submodule (see #88074), scope it to that case: recurse only when a plugin's manifest points skills / commands / agents at a path inside a submodule, and never for the marketplace container repository.

Two smaller requests:

  1. Print the full clone error. The truncation to Failed to clon… is what makes this failure undiagnosable from the CLI; the actual git error is the useful part.
  2. Give claude plugin marketplace update a -y / non-interactive path, so it does not hang when stdin is not a TTY.

Workaround

Repair the marketplace checkout by hand so it matches the remote, which satisfies the staleness probe without re-cloning:

git -C ~/.claude/plugins/marketplaces/chrome-devtools-plugins fetch --depth=1 origin main
git -C ~/.claude/plugins/marketplaces/chrome-devtools-plugins reset --hard origin/main
claude plugin update chrome-devtools-mcp@chrome-devtools-plugins -y

This has to be repeated whenever the checkout drifts from the remote again, since the probe re-clones on drift rather than fetching incrementally.

Related

  • ChromeDevTools/chrome-devtools-mcp#2829 — same submodule clone, reported as the clone timeout; closed as completed, though the troubleshooting guide still does not mention the submodule or the timeout
  • anthropics/claude-code#88074 — the opposite complaint: a plugin install did not clone its submodules, breaking a plugin that keeps its skills in one. It was closed by an automated inactivity response and another participant noted the closure looked incorrect. Worth reading before changing this behaviour, since it shows submodule support is load-bearing for at least one published plugin.

View original on GitHub ↗