claude plugin marketplace add fails with EPERM rename on Windows (clone-then-rename race with Defender / Indexer)
Symptom
claude plugin marketplace add <owner>/<repo>@<branch> fails on Windows with:
✘ Failed to add marketplace: Failed to finalize marketplace cache.
Please manually delete the directory at
C:\Users\user\.claude\plugins\marketplaces\<slug> if it exists and try again.
Technical details: EPERM: operation not permitted, rename
'C:\Users\user\.claude\plugins\marketplaces\<owner>-<repo>'
-> 'C:\Users\user\.claude\plugins\marketplaces\<slug>'
Root cause (suspected)
CC's add-from-GitHub flow appears to be:
git cloneto<owner>-<repo>/(intermediate name)renameto<slug>(marketplace's declared name)
On Windows, immediately after git clone finishes, file handles are still held by Windows Defender (real-time scan of newly-written files) and/or Windows Search Indexer for ~1-3 seconds. The synchronous rename in step 2 hits this lock window and fails with EPERM.
Reproduction
Consistent on Windows 11 + Claude Code 2.1.139 + Node v24.12.0. Tested case: zz41354899/SwiftMographer@main. Both retries and pre-creating the destination directory fail (CC cleans up source + dest on each failure, so retry has nothing to recover from).
Workaround (verified working)
claude plugin marketplace add accepts a local path as <source> (per --help), bypassing the clone+rename two-step:
mkdir -p C:/Users/user/.local-marketplaces
git clone --depth 1 --branch main https://github.com/<owner>/<repo>.git \
C:/Users/user/.local-marketplaces/<slug>
claude plugin marketplace add C:/Users/user/.local-marketplaces/<slug>
This works first try. Drawback: the local source doesn't track the upstream — user must git pull + claude plugin marketplace update <slug> manually.
Suggested fixes (in order of preference)
- Retry the rename with backoff (e.g., 100ms → 500ms → 2s, max 3 attempts). Defender lock typically releases within 1-2 seconds.
- Clone directly into the final slug name instead of clone-then-rename. This requires reading the marketplace's
namefield via shallow fetch or pre-clone introspection, but eliminates the rename step entirely. - Detect EPERM specifically and surface the local-path workaround in the error message (current message says "manually delete" which doesn't help — both source and dest are gone after auto-cleanup).
Mac / Linux note
POSIX rename doesn't fail on open file descriptors, so this only manifests on Windows. macOS / WSL users won't hit it.
Environment
- Claude Code 2.1.139
- Windows 11 Pro 10.0.26200
- Node v24.12.0
- Git 2.x (Git for Windows)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗