[BUG] "Windows auto-updater EBUSY: running 245MB SEA locks its dir during swap"

Open 💬 3 comments Opened Jun 12, 2026 by leonardoschepis-rgb

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Windows auto-updater fails with EBUSY ("file used by another process") and corrupts/orphans the install — running 245 MB SEA locks its own directory during dir-swap

Environment

  • OS: Windows 11 Pro (10.0.26200)
  • Install method: npm global, custom prefix (C:\claude-npm)
  • Version: 2.1.175 (also observed on 2.1.173 → 2.1.175)
  • Antivirus: Microsoft Defender, Real-Time Protection ON, no exclusion for the install dir
  • Node: system Node (C:\Program Files\nodejs)

Summary

On Windows, the in-session auto-updater intermittently fails and, historically, has corrupted the install. Root cause appears structural: the CLI ships as a ~245 MB single-file executable (Node SEA) that holds a mandatory file lock on its own image while running. The updater stages the new version into a sibling directory .claude-code-<random> and then attempts a directory swap. On Windows a directory containing an open file cannot be renamed or deleted, so the swap fails with EBUSY/EPERM ("being used by another process") whenever a claude.exe instance is alive. The result is a failed update plus a leftover staging directory.

Observed evidence (single machine)

  • The platform binary is 244,979,872 bytes at both:
  • …\@anthropic-ai\claude-code\bin\claude.exe
  • …\@anthropic-ai\claude-code\node_modules\@anthropic-ai\claude-code-win32-x64\claude.exe

These share an inode (hardlink, link count 2) — one large SEA, two names.

  • A live claude.exe (the running session) keeps a handle open on that image for its entire lifetime.
  • After a failed update, an orphaned staging directory …\@anthropic-ai\.claude-code-<random>\ (≈233 MB, containing a full claude-code-win32-x64\claude.exe) is left behind and never garbage-collected. Observed a leftover from the previous day still on disk.
  • Historically (older updater, ~v before the staging pattern) the same lock caused a partial in-place replace, leaving the install in a broken state (claude.cmd found but failed --version check), which cascaded into MCP server failures.

Why it "sometimes works"

The swap succeeds only when no other claude.exe holds the directory at the moment of promotion. With an in-session auto-updater — which runs inside the very process whose binary it must replace — that condition is impossible to guarantee, so failure is intermittent and, on Windows, structurally unavoidable.

Secondary factor

Defender Real-Time Protection opens the freshly written 245 MB executable for scanning, adding a transient lock window during promotion. Excluding the install dir reduces (but does not eliminate) intermittent failures.

Expected behavior

Updates complete atomically and never leave the install in a broken or partial state, even while one or more claude.exe instances are running.

Suggested fixes (in order of robustness)

  1. Versioned install + junction/symlink swap. Install each version under …\claude-code\versions\<version>\ and point a current junction at it. "Promotion" = recreate the junction (atomic, touches no open file). Running processes keep their version; new sessions follow the junction. Mirrors Chrome/VS Code Application\<version>\.
  2. Per-file rename-aside instead of per-directory swap. On Windows an open file can be renamed (only delete/overwrite is blocked); a directory containing an open file cannot. Rename the live claude.exe aside (e.g. claude.exe.old-<pid>), write the new one in place, and GC the renamed file once no process holds it.
  3. Deferred swap on next clean launch. If a live instance blocks promotion, keep the staged version and complete the swap at the next startup when no claude.exe is running (Chrome's pending-update model).
  4. Staging-dir garbage collection. Always clean orphaned .claude-code-<random> directories on startup; today they accumulate at ~233 MB each.

Workarounds currently in use

  • DISABLE_AUTOUPDATER=1, update manually only with zero running instances (npm i -g @anthropic-ai/claude-code).
  • Add-MpPreference -ExclusionPath "<install dir>".
  • A SessionStart hook that detects a truncated claude.exe (<100 MB) and GCs orphaned staging dirs.

What Should Happen?

Autoactualice correctamente

Error Messages/Logs

Steps to Reproduce

update-bug-issue.md

Claude Model

Sonnet (default)

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.175

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Windows Terminal

Additional Information

_No response_

View original on GitHub ↗

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