EnterWorktree bypasses PreToolUse hooks — executes filesystem ops before hooks fire

Resolved 💬 4 comments Opened Mar 17, 2026 by TGiles Closed Apr 16, 2026

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

  1. Configure a PreToolUse hook for EnterWorktree in settings.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
          }
        ]
      }
    ]
  }
}
  1. Ask Claude to create a worktree (e.g., "Create a worktree named test-branch")
  2. Check /tmp/worktree-hook-debug.log

Expected Behavior

  • The PreToolUse hook fires before any filesystem operations
  • The hook returns a deny decision
  • The tool call is blocked; Claude sees the permissionDecisionReason
  • No filesystem side effects occur

Actual Behavior

  • The PreToolUse hook never fires (log file is never created)
  • The tool immediately attempts chdir to 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 ExitWorktree to 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/WorktreeRemove hooks configured (jj-based, not git)
  • Reproduced with both Sonnet and Opus models (model is irrelevant — this is a tool execution ordering issue)

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗