[BUG] claude update / install.ps1 fail with EEXIST mkdir on ReadOnly directories (bundled Bun bug) — corrects the diagnosis in #67692

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

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

claude update (and the native install.ps1) abort with EEXIST: file already exists, mkdir '<dir>' on directories that already exist and carry the NTFS ReadOnly attribute — the same bundled-Bun mkdir(..., {recursive: true}) bug already documented for /export in #80669 and for session-env in #37306 / #50773 / #59622.

The install/update code path is a separate, still-unguarded call site, and it is the worst-hit one: the failure is not degraded functionality but a hard inability to update or install at all.

Two distinct paths fail, in sequence:

  1. <XDG_DATA_HOME>\claude\versions
  2. <XDG_STATE_HOME>\claude\locks

This report corrects the diagnosis in #67692. That issue (mine, closed as stale) attributed the failure to orphaned OneDrive Files-On-Demand placeholders — directory entries listed by dir but failing stat. That theory is wrong. On the machine here today:

  • No orphaned placeholders were present. Every entry under share\claude, share\claude\versions, state\claude and bin returned from both dir /A /B enumeration and Get-Item -LiteralPath. The update still failed identically.
  • The ~/.local → OneDrive directory symlink is irrelevant. Setting XDG_DATA_HOME / XDG_STATE_HOME to the direct, non-symlinked OneDrive path produced the same EEXIST on the same directory.
  • The ReadOnly attribute is the sole trigger, confirmed by bisect (below). OneDrive is only incidental: it stamps R on the directories it syncs, which is why the bug correlates with OneDrive in #67692 and #52161.

What Should Happen?

claude update should succeed against an existing directory regardless of its ReadOnly attribute, as Node's documented mkdir recursive semantics require.

Suggested fix — the same guard proposed for the session-env call site in #59622, applied to the installer/updater mkdirs:

if (!existsSync(dir)) await mkdir(dir, { recursive: true });
// or: await mkdir(dir, { recursive: true }).catch(e => { if (e.code !== 'EEXIST') throw e });

Longer term, bumping the bundled Bun past the fix for oven-sh/bun#34413 (closed upstream) removes this whole class of Windows EEXIST bugs at once.

Secondary: the remediation hints are dead ends for this failure. install.ps1 prints Try running with --force to override checks but does not forward the flag, and claude.exe install --force fails identically (#67692); claude update prints Try running "claude doctor" for diagnostics, which does not surface the ReadOnly attribute.

Error Messages/Logs

PS> claude update
Current version: 2.1.215
Checking for updates to latest version...
Updating to 2.1.219...
Error: Failed to install native update
Error: EEXIST: file already exists, mkdir 'C:\Users\<user>\.local\share\claude\versions'
Try running "claude doctor" for diagnostics

Same failure with the symlink removed from the path (XDG vars pointed at the real OneDrive location):

PS> $env:XDG_DATA_HOME="C:\Users\<user>\OneDrive\.local\share"
PS> $env:XDG_STATE_HOME="C:\Users\<user>\OneDrive\.local\state"
PS> claude update
Error: EEXIST: file already exists, mkdir 'C:\Users\<user>\OneDrive\.local\share\claude\versions'

Bisect — clearing ReadOnly on the first directory moves the error to the next one, then clearing both fixes it:

PS> (Get-Item "$env:USERPROFILE\.local\share\claude\versions" -Force).Attributes
ReadOnly, Directory, Archive, ReparsePoint

PS> attrib -R "$env:USERPROFILE\.local\share\claude\versions"
PS> claude update
Error: EEXIST: file already exists, mkdir 'C:\Users\<user>\.local\state\claude\locks'    <- moved on

PS> attrib -R "$env:USERPROFILE\.local\state\claude" ; attrib -R "$env:USERPROFILE\.local\state\claude\*" /S /D
PS> claude update
Successfully updated from 2.1.215 to version 2.1.219

Control — stock Node v24.18.0 does not reproduce it on the exact same paths while they still had ReadOnly set (mode is 444, i.e. the R attribute is present and observable):

{"path":"C:\\Users\\<user>\\.local\\share\\claude\\versions","mkdir":"OK","stat":"OK dir=true mode=444","write":"OK"}
{"path":"C:\\Users\\<user>\\OneDrive\\.local\\share\\claude\\versions","mkdir":"OK","stat":"OK dir=true mode=444","write":"OK"}
{"path":"C:\\Users\\<user>\\.local\\state\\claude\\locks","mkdir":"OK","stat":"OK dir=true mode=444","write":"OK"}

Node succeeds where the shipped binary fails, which places the defect in the bundled runtime rather than in libuv generally — consistent with the Bun analysis in #80669.

Steps to Reproduce

  1. Windows 11, native install.
  2. attrib +R "%USERPROFILE%\.local\share\claude\versions" (or let OneDrive stamp R by keeping the XDG data/state dirs in a synced folder).
  3. Run claude update when an update is actually available → EEXIST: file already exists, mkdir '...\share\claude\versions'.
  4. attrib -R the directory → claude update succeeds.

Step 3 needs a pending update; when already on the latest version the updater short-circuits before the mkdir.

Workaround for anyone hitting this:

attrib -R "%USERPROFILE%\.local\share\claude" & attrib -R "%USERPROFILE%\.local\share\claude\*" /S /D
attrib -R "%USERPROFILE%\.local\state\claude" & attrib -R "%USERPROFILE%\.local\state\claude\*" /S /D
claude update

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Claude Code Version

2.1.219 (Claude Code), native win32-x64

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

  • Environment: Windows 11 Pro 10.0.26200, PowerShell 7, OneDrive with Files-On-Demand. ~/.local is a directory symlink to %USERPROFILE%\OneDrive\.local for cross-desktop dotfile sync — but as shown above the symlink is not a factor.
  • Related: #80669 (/export, same root cause, open), #67692 (this same installer/updater failure, misdiagnosed as orphaned placeholders, closed as stale), #37306 / #50773 / #59622 (~/.claude and session-env, closed as duplicate or not-planned), #52161, oven-sh/bun#34413.
  • After the fix, ReadOnly had not been re-applied to those directories within the following ~10 minutes, so this does not appear to be a tight loop with the sync engine — but the attribute will presumably return, and the update will break again, on any machine where a sync or backup tool stamps R.

🤖 Diagnosis and report produced with Claude Code

View original on GitHub ↗