Windows: per-second statusLine bash spawns turn the MSYS add_item/errno 1 race into a permanent outage of every Bash call
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (#30165 is the same symptom, but it was auto-closed as inactive and locked without a root cause; this report has the root cause and fixes Claude Code can make on its own side)
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
On Windows, every Bash tool call, the status line, and git's credential helper all start dying with
Exit code 5
0 [main] bash (9800) C:\Program Files\Git\bin\..\usr\bin\bash.exe: *** fatal error - add_item ("\??\C:\Program Files\Git", "/", ...) failed, errno 1
after a 15 second hang. Once it starts it doesn't stop until every MSYS process on the machine is gone, which in practice means a reboot. #30165 reported the symptom. This is the mechanism, and why Claude Code's own status line polling is what turns a rare MSYS race into a permanent outage.
The MSYS bug (msys2-runtime, same code in 3.4.7 and 3.6.10; tracked as git-for-windows/git#6368, fix pending in msys2/msys2-runtime#333). Every MSYS process (bash.exe, sh.exe, ...) maps a per-user shared memory section at startup. The first one to map it builds the mount table under a spinlock that gives up after 15 s and lets the waiter initialise anyway. The section only exists while some MSYS process has it mapped. If the initialiser dies inside the critical section (a process tree being terminated while a shell is starting, or an init that takes more than 15 s), the lock is stuck at -1. Every newcomer then waits 15 s, re-runs mount_info::init on the already built table, and dies in create_root_entry with EPERM, without ever resetting the lock.
Why Claude Code makes it permanent. On Windows a statusLine command runs as bash.exe -c "<command>" through Git Bash, once per refresh, per session. With refreshInterval: 1 and three sessions open that is three fresh MSYS cold starts every second, and each crashing victim spins for 15 s before it dies. So there is always a new holder alive when the last one dies, and the poisoned section is never released. I checked this by enumerating processes with msys-2.0.dll loaded: nothing but 15-second-old bash processes, all -c "node ~/.claude/statusline.mjs", for as long as I watched. With nothing long-lived around, the section is also being torn down and rebuilt many times a minute, so every rebuild is a fresh chance to lose the race in the first place.
Downstream: git's credential.helper = manager runs through sh -c, so HTTPS fetch/push fail with "could not read Username"; every Bash tool call fails; hooks in shell form fail.
What Should Happen?
Any one of these would fix it from Claude Code's side, without waiting for the runtime patch:
- Keep one long-lived MSYS process alive while any session is running (for example
usr\bin\sleep.exe infinity, non-elevated). The shared section then gets initialised once, by a process nothing is racing, and every laterbashstart takes the fast path (locktest == wanted_val, no spinning). This is the usual CI workaround for this bug (keep a common MSYS parent alive), and it's what I run locally now as a logon task: cold starts went to 28 to 37 ms and it hasn't come back. - Let
statusLinerun in exec form, with theargsarray hooks already support, so anodeorpowershellstatus line doesn't need abashwrapper on Windows at all. The 2.1.278 schema only acceptstype,command,padding,refreshIntervalandhideVimModeIndicator. - Recognise the fatal and recover: when a spawned
bashexits with this exact message, kill every process withmsys-2.0.dllloaded (in the poisoned state all of them are already doomed) so the section is released, instead of retrying into the same wall every second.
Steps to Reproduce
Hard to trigger on purpose from outside the DLL, but this shape reliably produces it:
- Windows 11, Git for Windows, a
statusLinewithrefreshInterval: 1, three or more sessions open. - Run an orchestration that terminates worker sessions' process trees while they are busy (shells starting and stopping), under CPU load.
- At some point one
bashstart dies with theadd_item ... errno 1fatal after a 15 s hang, and from then on every one does.Get-Process | ? { $_.Modules.ModuleName -contains 'msys-2.0.dll' }shows nothing but youngbashprocesses. - Kill every MSYS process (or reboot) and it works again. Stop the status line churn and the poisoned state clears within 15 s on its own; keep it running and it never does.
Claude Code Version
2.1.278
Operating System
Windows 11 Pro 10.0.26100, not domain-joined (so this is not the slow AD lookup variant in git-for-windows/git#6368)
Terminal/Shell
Windows Terminal, Git for Windows 2.42.0.windows.2 (msys2-runtime 3.4.7)
Additional Information
Related: #30165 (same symptom, closed and locked), git-for-windows/git#6368 (mechanism, slow lookup trigger), msys2/msys2-runtime#333 (runtime fix, waiting on upstream submission to cygwin-patches).
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗