/plugin install fails on Windows with EBUSY rename errors (Defender real-time scan race)

Open 💬 5 comments Opened Jun 11, 2026 by chadj2

Bug: /plugin install fails on Windows with EBUSY rename errors (Defender real-time scan race)

Environment

  • OS: Windows 11 Enterprise (10.0.26100)
  • Claude Code: CLI on Windows (PowerShell)
  • Windows Defender real-time protection: ON (enterprise-managed)
  • User privileges: non-admin (cannot add a Defender exclusion or disable real-time scanning)
  • Corporate network: outbound SSH (port 22) blocked

Summary

Installing a marketplace plugin on locked-down enterprise Windows fails repeatedly. Two distinct issues compound:

  1. SSH transport is assumed. /plugin install clones plugin repos over git@github.com: (SSH/port 22). On corporate networks that block port 22 this fails with ssh: connect to host github.com port 22: Connection timed out. HTTPS (443) works fine.
  2. Clone-then-rename + git checkout race against Defender. The installer clones into a temp dir then performs an atomic rename into the cache, and git checkout uses HEAD.lockHEAD renames. On Windows, a rename fails with EBUSY when any process holds a handle on the file — and Defender's real-time scanner grabs handles on freshly-written files. This produces intermittent failures at different steps:
  • EBUSY: resource busy or locked, rename '...temp_github_*' -> '...cache\<marketplace>\<plugin>\<ver>'
  • Failed to checkout commit <sha>: fatal: update_ref failed for ref 'HEAD': couldn't set 'HEAD'

The same class of failure also hit /plugin marketplace add (EBUSY ... rename 'avivsinai-skills-marketplace' -> 'avivsinai-marketplace').

Steps to reproduce

On enterprise Windows with Defender real-time protection ON and no admin rights:

  1. /plugin marketplace add <owner>/<repo> (may need several retries due to EBUSY on the cache rename)
  2. /plugin install <plugin>@<marketplace>

Expected

Plugin installs reliably over HTTPS without requiring SSH/port 22 or admin-level AV exclusions.

Actual

Fails intermittently at the rename or git-checkout step. enabledPlugins gets written to settings.json, but the plugin files never land in the cache, so the plugin is "enabled" yet absent. Retrying sometimes succeeds (the lock is transient) but is unreliable; leftover temp_github_* dirs accumulate in ~/.claude/plugins/cache.

Root cause

The temp-clone-then-rename pattern and git's lock-file renames are POSIX-friendly but Windows-hostile: POSIX allows rename over/with open handles; Windows returns EBUSY. Real-time AV scanning of freshly-written files makes the race fire frequently.

Suggested fixes

  1. Prefer HTTPS for GitHub clones (or detect SSH failure and fall back to HTTPS), rather than defaulting to git@github.com:.
  2. Retry filesystem rename / git checkout on Windows EBUSY with short backoff — transient AV locks clear within a second or two.
  3. Copy-into-place instead of atomic rename on Windows, or write directly to the final path. A file-by-file copy has no whole-directory rename to fail.
  4. Clean up temp_github_* dirs on failure so retries start clean.
  5. On a failed install, roll back the enabledPlugins entry (or mark it pending) so state doesn't claim a plugin is enabled when its files are absent.

Workarounds that worked (for other affected users)

  • Global git rewrite to dodge the port-22 block:

``
git config --global --add url."https://github.com/".insteadOf "git@github.com:"
git config --global --add url."https://github.com/".insteadOf "ssh://git@github.com/"
``

  • Manual install: clone the plugin repo over HTTPS, check out the pinned SHA, then robocopy the working tree into ~/.claude/plugins/cache/<marketplace>/<plugin>/<version> (copy avoids the failing rename). Restart Claude Code to load it.
  • Running Claude Code under WSL2 avoids the Windows rename semantics and AV race entirely.
  • A Defender exclusion on ~/.claude/plugins fixes it at the source, but requires admin/IT.

View original on GitHub ↗

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