remote-control --spawn worktree: default pre-created session dies with 404 and leaves a stale .claude/worktrees that permanently breaks all later spawns (EEXIST, Windows)
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?
On Windows, claude remote-control --spawn worktree bricks itself on first use, and stays bricked until you manually delete .claude/worktrees and strip a ReadOnly attribute off .git/worktrees/<name>.
The chain (all observed in one 30-minute session, v2.1.220):
- The default-on pre-created session dies with a 404.
--create-session-in-diris on by default, so the server pre-creates one session at startup. That session immediately failed withRemoteIO: transport closed permanently (code 404)and the harness explicitly kept its worktree (· session crashed). The server process itself was fine — it printedReady, the session URL and the QR code, so nothing signals that anything needs cleaning up.
- The leftover directory then permanently breaks every subsequent worktree spawn. On the next run, creating a session from the mobile app fails with
EEXIST: file already exists, mkdir '...\.claude\worktrees'— the parent directory, not the session's own subdirectory. This is the same non-idempotentmkdirroot cause as #70313 (--worktreeflag) and the subagent-isolation reports (#39680, #51023, #54706, #64519), now reproducing on a third surface: remote-control spawn.
- From the phone, this failure is invisible. The session gets created server-side and appears in the session list at claude.ai/code, but it has no local worktree, so it simply never responds. No error is surfaced to the remote client. You just get a session that is silently dead.
- Recovery is harder than it should be.
git worktree remove --forcederegistered the worktree but could not delete the files (repeatedDeletion of directory ... failed. Should I try again? (y/n)prompts).git worktree prunethen failed witherror: failed to delete '.git/worktrees/<name>': Permission denied, because the kept worktree's admin directories carry the ReadOnly attribute (cf. #37306). Recovery required clearing the attribute and deleting.git/worktrees/<name>by hand.
Once .claude/worktrees was fully removed, worktree spawn worked on the first try: Capacity: 1/32, worktree registered, and a prompt sent from the mobile app was answered normally. So the feature itself is fine — it is the leftover state that is fatal.
What Should Happen?
- Creating the worktree parent should be idempotent:
mkdir(..., { recursive: true })(or an existence check) so a pre-existing.claude/worktreesis not fatal. This one-line class of fix has now been reported for--worktree, subagent isolation, and remote-control spawn. - A crashed pre-created session should not leave state that permanently disables the feature. Either clean up the worktree it kept, or tolerate the leftover on the next spawn.
- A worktree that is deliberately kept for post-mortem should not be left with ReadOnly admin dirs that make
git worktree prunefail. - When a spawn fails locally, the remote client should be told. A session that can never respond should not sit in the session list looking healthy.
Error Messages/Logs
Run 1 — defaults (pre-created session on). Server reports Ready regardless:
PS C:\Users\<user>\Documents\GitHub\<repo>> claude remote-control --spawn worktree
Enable Remote Control? (y/n) y
[18:52:22] Session failed: RemoteIO: transport closed permanently (code 404) cse_<redacted>
[18:52:22] kept worktree C:\Users\<user>\Documents\GitHub\<repo>\.claude\worktrees\bridge-cse_<redacted> · session crashed
·✔︎· Ready · <repo>
Capacity: 0/32 · New sessions will be created in an isolated worktree
Run 2 — leftover directory present; spawning a session from the mobile app:
PS C:\Users\<user>\Documents\GitHub\<repo>> claude remote-control --spawn worktree --no-create-session-in-dir --verbose
Remote Control v2.1.220
Spawn mode: worktree
Max concurrent sessions: 32
Environment ID: env_<redacted>
[19:07:32] Error: Failed to create worktree for session cse_<redacted>: EEXIST: file already exists, mkdir 'C:\Users\<user>\Documents\GitHub\<repo>\.claude\worktrees'
The session exists at claude.ai/code/session_<redacted> but never responds.
Cleanup blocked by ReadOnly admin dirs:
PS> git worktree remove --force .claude/worktrees/bridge-cse_<redacted>
Deletion of directory '.../.claude/worktrees/bridge-cse_<redacted>/.github/workflows' failed. Should I try again? (y/n) y
Deletion of directory '.../.claude/worktrees/bridge-cse_<redacted>/.github/workflows' failed. Should I try again? (y/n) y
... (repeats indefinitely)
PS> git worktree prune
error: failed to delete '.git/worktrees/bridge-cse_<redacted>': Permission denied
PS> Get-ChildItem .git\worktrees -Recurse -Force | Select-Object Name, Attributes
Name Attributes
---- ----------
bridge-cse_<redacted> ReadOnly, Directory
logs ReadOnly, Directory
refs ReadOnly, Directory
ORIG_HEAD Archive
Run 3 — after Remove-Item -Recurse -Force .claude\worktrees and manual removal of .git\worktrees\<name>:
[19:18:03] Session started: "Session cse_<redacted>" (cse_<redacted>)
·✔︎· Connected · <repo>
Capacity: 1/32 · New sessions will be created in an isolated worktree
Worktree registered correctly and the mobile-sent prompt was answered.
Steps to Reproduce
- On Windows,
cdinto a git repo and runclaude remote-control --spawn worktree(defaults, i.e.--create-session-in-diron). - Observe the pre-created session fail with
transport closed permanently (code 404)and keep its worktree under.claude/worktrees/. The server still reportsReady. - Stop the server. Restart it (with or without
--no-create-session-in-dir). - From the Claude mobile app or claude.ai/code, create a session in that environment.
- ❌ Terminal:
Failed to create worktree for session ...: EEXIST: file already exists, mkdir '...\.claude\worktrees'. The session appears in the session list but never responds. - Every later attempt fails identically until
.claude/worktreesis deleted.
Step 2's 404 is what seeds the leftover in the wild, but step 5 reproduces from any pre-existing .claude/worktrees directory — for example one left behind by the git worktree remove exit-255 lock problem described in #76590.
Claude Model
Sonnet 5
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.220 (Claude Code), native install
Platform
claude.ai (subscription OAuth login)
Operating System
Windows 11 (10.0.26200), NTFS
Terminal/Shell
PowerShell 7.6.4 / Windows Terminal
Additional Information
Related — same non-idempotent mkdir('.claude/worktrees') root cause on other surfaces:
- #70313 —
--worktreeflag, EEXIST on second worktree creation (open) - #39680, #51023, #54706, #64519 — Agent tool
isolation: "worktree"/ subagent worktrees (closed; #64519 notes the recurrence explicitly) - #37306 — Windows EEXIST/ReadOnly-attribute interaction
- #76590 — Windows: stale non-empty
.claude/worktreesreused by desktop session creation, and thegit worktree removeexit-255 lock loop that produces leftovers
Notes:
--verboseon remote-control server mode floods the terminal with the raw NDJSON protocol stream (eachcontrol_responsecarries the full slash-command, agent and model catalogs) and re-renders the QR block on every event, which makes diagnosing anything in that mode impractical. The per-session debug log at%TEMP%\claude\bridge-session-<id>.logis far more usable; consider not mirroring the full protocol stream to stdout.--spawn worktreealso requires being inside a git repository, which is easy to miss: starting the server from a non-repo directory (e.g. the home directory) still printsReady, and only fails later at spawn time.