WorktreeRemove hook never fires when a worktree is removed at session exit
Environment
- Claude Code 2.1.201 (native installer, latest at time of writing)
- macOS 26.5 (reproduced identically regardless of exit path)
Description
The hooks reference documents that WorktreeRemove "fires when a worktree is being removed, either at session exit or when a subagent finishes." In 2.1.201 the hook is never invoked at session exit — neither when an unchanged worktree is removed automatically, nor when the user answers the remove/keep prompt for a changed worktree with "remove".
The worktree's branch is also left behind in both cases, although the worktrees doc says removal deletes the branch ("the worktree and its branch are removed automatically").
Since WorktreeRemove output is only visible in debug mode, this fails silently: any external resources a WorktreeCreate hook provisions (databases, containers, ports) leak on every worktree session.
Steps to reproduce
- In a git repo, add to
.claude/settings.json:
{
"hooks": {
"WorktreeRemove": [
{ "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/remove-worktree.sh" }] }
]
}
}
- Create the hook script (
chmod +x):
#!/bin/sh
cat >> /tmp/worktree-remove-hook.log
echo "fired at $(date)" >> /tmp/worktree-remove-hook.log
- Run
claude --worktree test, then exit the session (/exit). - Claude Code prints
Worktree removed (no changes)and the worktree directory is gone. /tmp/worktree-remove-hook.logwas never created. Repeating with a modified file in the worktree and choosing "remove" at the exit prompt gives the same result.
Expected
The WorktreeRemove hook runs with {"hook_event_name": "WorktreeRemove", "worktree_path": ...} on stdin before/when the worktree is removed, and the worktree's branch is deleted, as documented.
Actual
The worktree directory is removed and deregistered, the hook never runs, and the branch remains.
Notes
- Other hooks fire normally in the same session (
WorktreeCreateruns at startup; theStophook runs at exit — visible in the exit output), so this isn't a hooks-config problem. - The
WorktreeRemoveevent schema and executor exist in the CLI bundle (the stringWorktreeRemove hook failed [...]is present), so it appears the executor simply isn't called from the session-exit cleanup path. - Possibly related docs mismatch: in 2.1.201 the
WorktreeCreatestdin payload contains onlyname(plus common fields), while the docs describeworktree_path,branch, anddetached.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗