EnterWorktree bypasses PreToolUse hooks — executes filesystem ops before hooks fire
Summary
PreToolUse hooks with matcher "EnterWorktree" never execute. The EnterWorktree tool performs internal filesystem operations (specifically chdir to the parent directory) before PreToolUse hooks are evaluated, causing the tool to fail or succeed without hook intervention.
Steps to Reproduce
- Configure a
PreToolUsehook forEnterWorktreeinsettings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "EnterWorktree",
"hooks": [
{
"type": "command",
"command": "bash -c 'echo \"hook fired\" >> /tmp/worktree-hook-debug.log; echo \"{\\\"hookSpecificOutput\\\":{\\\"hookEventName\\\":\\\"PreToolUse\\\",\\\"permissionDecision\\\":\\\"deny\\\",\\\"permissionDecisionReason\\\":\\\"Denied by hook\\\"}}\";'",
"timeout": 5
}
]
}
],
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'echo /tmp/test-worktree'",
"timeout": 60
}
]
}
]
}
}
- Ask Claude to create a worktree (e.g., "Create a worktree named test-branch")
- Check
/tmp/worktree-hook-debug.log
Expected Behavior
- The
PreToolUsehook fires before any filesystem operations - The hook returns a
denydecision - The tool call is blocked; Claude sees the
permissionDecisionReason - No filesystem side effects occur
Actual Behavior
- The
PreToolUsehook never fires (log file is never created) - The tool immediately attempts
chdirto the parent directory (../) - On Windows/MSYS2, this fails with:
````
ENOENT: no such file or directory, chdir 'G:\mozilla\jj_firefox\firefox\' -> '/g/mozilla/jj_firefox/firefox/../'
- Despite the error, internal session state is set to "in a worktree," requiring
ExitWorktreeto recover
Secondary Bug: Stale worktree state on failure
When EnterWorktree fails (ENOENT or otherwise), the session's internal "in worktree" flag is still set to true. Subsequent EnterWorktree calls return "Already in a worktree session" even though no worktree was created. The only recovery is calling ExitWorktree.
Verification
The hook command itself works correctly when run manually:
CLAUDE_TOOL_INPUT='{"name":"test-branch"}' bash -c '<hook command>'
# Produces valid JSON deny output
The issue is purely in execution ordering — the tool acts before hooks are consulted.
Impact
- PreToolUse hooks for EnterWorktree are completely non-functional. Any validation, gating, or logging configured via PreToolUse on this tool will silently never run.
- WorktreeCreate hooks may still work (if the chdir succeeds), but cannot be gated by PreToolUse.
- The stale state bug compounds the issue — a failed call locks the session until manually cleared.
Environment
- Windows 11 Pro (MSYS2 bash shell)
WorktreeCreate/WorktreeRemovehooks configured (jj-based, not git)- Reproduced with both Sonnet and Opus models (model is irrelevant — this is a tool execution ordering issue)
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗