Hook spawn fails with ENOENT when session CWD is deleted
Summary
When the session's working directory is deleted (e.g., after a git worktree removal), all subsequent hook commands fail with:
ENOENT: no such file or directory, posix_spawn '/bin/sh'
The error occurs at the OS level — posix_spawn cannot resolve the process's CWD, so /bin/sh never starts. This means no shell-level mitigation (e.g., cd /valid/path; prefix in the hook command) can help, because the shell process itself cannot be created.
Reproduction
- Start a Claude Code session in a git repository
- Use
EnterWorktreeto move the session into a linked worktree - Run an operation that removes that worktree directory (e.g.,
git worktree remove) - Trigger any hook event (e.g., stop the session)
- Observe:
ENOENT: no such file or directory, posix_spawn '/bin/sh'
The error repeats on every subsequent hook invocation for the remainder of the session.
Root Cause
child_process.spawn('/bin/sh', ['-c', command]) inherits the process's CWD. When that CWD no longer exists on disk, the kernel rejects the posix_spawn syscall before the shell binary is even loaded.
Suggested Fix
Before spawning hook commands, check whether the current CWD exists and fall back to a valid directory if not:
const cwd = fs.existsSync(process.cwd()) ? process.cwd() : os.homedir();
child_process.spawn('/bin/sh', ['-c', command], { cwd });
This would allow hook commands to execute even when the session's original CWD has been removed. The hook command itself can then cd to whatever directory it needs.
Environment
- macOS (Darwin 25.3.0)
- Claude Code 2.1.80
- Triggered by git worktree removal mid-session
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗