macOS: intermittent 2-minute git hangs with core.fsmonitor enabled (multi-worktree/concurrent sessions); timeout SIGTERM orphans index.lock and wedges the session
Environment
- Claude Code 2.1.204, macOS 26.5.2 (Darwin 25.5.0), Apple Silicon
- git 2.50.1 (Apple Git-155)
- Repos under
~/GitHub/<repo>with session worktrees under<repo>/.claude/worktrees/<slug>; typically several concurrent Claude Code sessions working the same repo through linked worktrees - Auto permission mode (sandboxed Bash)
Summary
With core.fsmonitor=true set, ordinary git commands inside Bash tool calls intermittently hang for the full 2-minute default timeout and die with exit 143 (SIGTERM). When the killed command was a git write, it leaves .git/worktrees/<slug>/index.lock behind, and every later git write in the session fails with fatal: Unable to create '...index.lock': File exists until the lock is manually removed — so one hung call wedges the rest of the session. Running git config core.fsmonitor false in the affected session immediately restored normal behavior, including a git fetch that had just timed out twice.
Incident timeline (2026-07-08, transcript timestamps UTC, one session)
- 12:02:52 —
git grep <term>on tracked files:Exit code 143 — Command timed out after 2m 0s - 12:11:36 — compound command containing
git fetch:Exit code 143 — Command timed out after 2m 0s - 12:14:16 — next git write:
Exit code 128 — fatal: Unable to create '.../.git/worktrees/<slug>/index.lock': File exists(no git process actually running; the lock was orphaned by the timed-out command) - 12:31–12:33 — compound containing
git status --short:Exit code 143 — Command timed out after 2m 0sagain; the model reports "my shell is unstable (SIGKILL/timeout on git)" - 12:44:36 — diagnosis in-session: "Fast git reads work; the 2-min hang was
git statusscanning the working tree (likely fsmonitor)" - 12:44:41 — remediation:
git config core.fsmonitor false,git config core.untrackedcache true,export GIT_OPTIONAL_LOCKS=0, remove the orphaned lock - 12:44:46 — the previously-hanging
git fetch origin maincompletes instantly (rc=0);git checkout -B <branch>succeeds; the session reports "everything ran clean after"
Prior independent incident (2026-06-02, different repo)
While diagnosing git index anomalies in a different repo on the same machine, a session found three stacked git fsmonitor--daemon run --detach --ipc-threads=8 processes for /Library/Developer/CommandLineTools/usr/libexec/git-core/git, which it attributed to "multiple worktrees/sessions".
Frequency
Scanning this machine's ~/.claude/projects transcripts: 30 sessions contain the Command timed out after marker, 8 sessions contain index.lock recovery work, and 2 sessions contain an explicit fsmonitor hang/diagnosis (2026-06-02 and 2026-07-08). The pattern spans at least five weeks and multiple repos.
What did and did not reproduce
A fresh repo under /private/tmp with core.fsmonitor=true, exercised inside a sandboxed Bash call, works fine: the daemon starts, git fsmonitor--daemon status reports watching, and git status returns in ~30ms. The hangs occurred on long-lived repos under ~/GitHub with a shared .git, multiple linked worktrees, and several concurrent Claude Code sessions. So the trigger looks like some combination of (a) multiple concurrent sessions racing to start/talk to the daemon across linked worktrees and (b) possibly sandbox restrictions on the daemon IPC socket (.git/fsmonitor--daemon.ipc) under $HOME when the daemon was started by a different process — rather than fsmonitor per se.
Impact and workaround
One hung git call wedges the session (timeout → orphaned index.lock → every write fails), and recovery burns many turns. Workaround that has held: git config --global core.fsmonitor false plus removing the orphaned lock.
Suggestions
- When the Bash tool times out and kills a
gitprocess, consider detecting/surfacing an orphanedindex.lock(or hinting at it in the timeout error). The current downstream failure mode —File existson every later write — is a confusing cascade. - Investigate whether the sandbox profile permits connecting to an existing
fsmonitor--daemonIPC socket under$HOMEwhen the daemon was spawned by another (unsandboxed or other-session) process, and whether git then blocks instead of failing fast. - Consider documenting
core.fsmonitor falseas the known-good configuration for multi-session / multi-worktree use on macOS.
Full sanitized transcript excerpts available on request.