Plugin marketplace auto-update rewrites .git/config of the user's current working directory
Plugin marketplace sync corrupts .git/config of the user's current working repo
Summary
When multiple plugin marketplaces are configured, Claude Code's marketplace auto-update appears to run git operations using the wrong working repository. As a result:
remote.origin.urlof the user's current working directory (a completely unrelated git repo) gets repeatedly rewritten to point at Claude marketplace repositories.- Duplicate
fetch = +refs/heads/master:refs/remotes/origin/masterlines accumulate in[remote "origin"]of the same.git/configon every cycle. - Duplicate
active = .lines accumulate under[submodule]on every cycle. - The marketplace install directories themselves (
~/.claude/plugins/marketplaces/<name>/.git/config) all end up with the same wrong URL — every git-based marketplace I have points its origin atjetbrains/phpstorm-claude-marketplace.git, regardless of which marketplace the directory actually contains.
Net effect: I can no longer git push / git fetch against my actual project remote until I manually rewrite .git/config, and the corruption returns periodically while Claude Code is running.
Environment
- Claude Code: 2.1.152
- macOS: 26.5 (Darwin 25.5.0, arm64)
- git: 2.53.0
- Shell: zsh
Marketplaces configured
~/.claude/plugins/known_marketplaces.json:
{
"claude-plugins-official": { "source": { "source": "github", "repo": "anthropics/claude-plugins-official" }, ... },
"dev-marketplace": { "source": { "source": "git", "url": "git@gitlab-shipmonk:shipmonk-company/platform/claude-marketplace-dev.git" }, "autoUpdate": true, ... },
"phpstorm-marketplace": { "source": { "source": "github", "repo": "jetbrains/phpstorm-claude-marketplace" }, "autoUpdate": true, ... },
"shipmonk-corporate": { "source": { "source": "git", "url": "git@github.com:shipmonk-rnd/claude-marketplace-corporate.git" }, "autoUpdate": true, ... }
}
Observed corruption — symptom A: cross-contaminated marketplace .git/config
After Claude Code has been running for a while:
$ for d in ~/.claude/plugins/marketplaces/*/; do
echo "=== $d ==="
echo "URL: $(git -C "$d" config --get remote.origin.url 2>/dev/null)"
done
=== .../claude-plugins-official/ === URL: # no .git, uses GitHub API
=== .../dev-marketplace/ === URL: git@github.com:jetbrains/phpstorm-claude-marketplace.git # ❌ wrong
=== .../phpstorm-marketplace/ === URL: git@github.com:jetbrains/phpstorm-claude-marketplace.git # ✓
=== .../shipmonk-corporate/ === URL: git@github.com:jetbrains/phpstorm-claude-marketplace.git # ❌ wrong
Two of the three git-based marketplace install directories have their remote.origin.url overwritten to point at a different marketplace's repo. They all "win" with whichever marketplace was synced last.
Observed corruption — symptom B: the user's CWD .git/config is also rewritten
I set up an fswatch on my project's .git/config (~/shipmonk/monorepo/.git/config) — an unrelated repo with no Claude marketplace anywhere near it. Captured diffs show the URL flipping between three values over the course of a Claude Code session:
- url = git@gitlab-shipmonk:shipmonk-company/shipmonk-fulfillment/shipmonk.git
+ url = git@gitlab-shipmonk:shipmonk-company/platform/claude-marketplace-dev.git
- url = git@gitlab-shipmonk:shipmonk-company/platform/claude-marketplace-dev.git
+ url = git@github.com:shipmonk-rnd/claude-marketplace-corporate.git
In the same window, an extra active = . keeps getting appended under [submodule] on every cycle:
[submodule]
active = .
+ active = .
And on a related variant of the bug, a duplicate fetch refspec accumulates in [remote "origin"]:
[remote "origin"]
url = ...
fetch = +refs/heads/*:refs/remotes/origin/*
fetch = +refs/heads/master:refs/remotes/origin/master # appended over and over
fetch = +refs/heads/master:refs/remotes/origin/master
fetch = +refs/heads/master:refs/remotes/origin/master
... (14× total observed)
Process snapshot taken at the moment fswatch fired
git -c credential.helper= -c core.quotepath=false -c log.showSignature=false ls-remote --heads origin master <my-feature-branches...>
parent: git remote-https origin https://github.com/anthropics/claude-plugins-official.git
git pull origin HEAD
parent: claude --dangerously-skip-permissions
effective ssh: git@github.com:jetbrains/phpstorm-claude-marketplace.git
So Claude Code is invoking git ls-remote / git fetch / git pull against marketplace URLs but with the user's branch names (master, jv-...) — strongly suggesting it's running these commands inside the user's CWD .git, not the marketplace install directory.
Reproduction
- Have multiple plugin marketplaces registered with
autoUpdate: true— at least onegithub-source and at least two distinctgit-source URLs. - Run
claudefrom inside an unrelated git repository (any project repo). - Leave Claude Code running and check
git config --get remote.origin.urlperiodically. - Within minutes, the URL gets swapped to one of the marketplace URLs, fetch refspecs duplicate, and
[submodule] active = .accumulates.
Expected behavior
- Marketplace sync should
git-operate inside each marketplace's own install directory (~/.claude/plugins/marketplaces/<name>), never touching the user's CWD.git. - Each marketplace install dir's
remote.origin.urlshould reflect that marketplace's configuredsource.url, not whichever marketplace was synced last. - Repeated syncs must be idempotent — no accumulation of duplicate
fetchrefspecs or[submodule] active = ..
Suspected root cause
Looks like the sync logic might be doing something like:
git remote set-url origin <marketplace-url>
git fetch
without -C <marketplace-dir> (or with cwd not switched), so the operation runs in whatever process.cwd() happens to be — i.e. the user's project. Additionally the URL is set-url'd into a shared variable / shared .git and not reset between marketplaces, which is why all three install dirs end up with the same URL.
Workaround
A shell function to undo the damage:
git-fix-fetch() {
local expected="<correct-origin-url>"
local wildcard="+refs/heads/*:refs/remotes/origin/*"
local current
current=$(git config --get remote.origin.url)
if [ "$current" != "$expected" ]; then
git remote set-url origin "$expected"
fi
# Reset ALL fetch refspecs and re-add only the wildcard
git config --unset-all remote.origin.fetch 2>/dev/null
git config --add remote.origin.fetch "$wildcard"
git fetch origin
}
This needs to be re-run every time the corruption returns, which is roughly every plugin auto-update cycle.
Logs
Happy to share full fswatch log and snapshots privately if helpful.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗