[BUG] plugin install still fails with EPERM on locked-file rename and leaves temp_local_* behind on Windows (2.1.220, despite the v2.1.186 fix)

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Jul 27, 2026

Summary

claude plugin install intermittently fails on Windows with EPERM: operation not permitted, rename when moving the staging directory into the plugin cache, and leaves the temp_local_* directory behind.

Both of these are what the v2.1.186 changelog entry says was fixed:

Fixed plugin cache writes leaving temp files behind on failure and failing on locked-file renames on Windows and network filesystems

I'm on 2.1.220 and still see both, so the fix looks incomplete rather than absent.

Environment

  • Claude Code 2.1.220 (native Windows install, %USERPROFILE%\.local\bin\claude.exe)
  • Windows 11 Enterprise 26200
  • Microsoft Defender real-time protection + a corporate proxy agent, both scanning on write
  • Plugin from a private GitHub-source marketplace, ~78 files

Reproduction

Failure is intermittent — 1 failure in 6 attempts in a controlled loop. Each iteration removed the versioned cache dir first, to force a fresh clone + rename:

for ($i = 1; $i -le 6; $i++) {
  Remove-Item "$HOME\.claude\plugins\cache\<marketplace>\<plugin>" -Recurse -Force
  claude plugin install <plugin>@<marketplace>
  "attempt $i exit=$LASTEXITCODE"
}
attempt 1 : exit=0
attempt 2 : exit=0
attempt 3 : exit=0
attempt 4 : exit=0
attempt 5 : exit=0
attempt 6 : exit=1   <-- failed

Exact error (attempt 6)

✘ Failed to install plugin "<plugin>@<marketplace>": EPERM: operation not permitted, rename
'C:\Users\<USER>\.claude\plugins\cache\temp_local_1785186411515_vjc8mk'
-> 'C:\Users\<USER>\.claude\plugins\cache\<marketplace>\<plugin>\1.40.1'

Two distinct problems

1. The rename still fails. Presumably the AV/scanner still holds a handle on the just-cloned tree when the rename fires. It reproduces far more readily on a large plugin (~78 files); a small plugin (~6 files) in the same marketplace has never failed for us — consistent with a scan-duration race.

2. temp_local_* is left behind on failure.

temp_local_1785186411515_vjc8mk   <-- still in plugins/cache/ after the failure

These accumulate silently and are never cleaned up.

Why this matters more than an occasional retry

A failed install de-registers the plugin from both registries — it disappears from installed_plugins.json and from enabledPlugins in settings.json. The result is not "install didn't happen", it's the previously-working plugin is now gone, and for a plugin that ships MCP servers the symptom is an empty /mcp with no obvious cause. We lost ~20 minutes to exactly this, misdiagnosing it as a Docker problem, because a disabled plugin's servers are absent from /mcp rather than shown as failed.

It's worse in combination with the documented cache workaround for #14061: any flow that clears the cache and reinstalls has a real per-run chance of leaving a plugin uninstalled.

Mitigating detail (possibly useful)

An immediate retry of the identical command usually succeeds — the staging dir has gone cold by then, so the scanner has released it. That suggests an internal retry-with-backoff around the rename would resolve most of these. Ordinary fs.rename on Windows fails immediately if any handle is open; a couple of retries ~100–500ms apart is the usual remedy.

Expected

  • The rename retries briefly before failing, rather than failing on first contention.
  • On genuine failure, temp_local_* is cleaned up.
  • A failed install does not leave the plugin removed from enabledPlugins — ideally the previous working state is preserved, or at minimum the error says the plugin is now uninstalled so the user knows to re-run.

Related

  • #14061 — /plugin update does not invalidate the plugin cache (open). The documented workaround is to clear the cache and reinstall, which is what exposes this bug repeatedly.
  • #17361 — plugin cache never refreshes; autoUpdate doesn't update what Claude reads (open).

View original on GitHub ↗