PreToolUse Bash hook with relative command path can permanently deadlock the Bash tool for the rest of a session

Status Open
Reported on v2.1.252
Maintainer reply None cached
Activity 0 comments · opened Sep 1, 2026

Summary

A PreToolUse hook on the Bash tool, registered in .claude/settings.json with a relative command path, breaks irrecoverably as soon as the Bash tool's working directory (which persists across tool calls within a session, per current documented behavior) moves away from the directory the relative path was written against. Once broken, every subsequent Bash call in that session fails — including trivial, unrelated commands like pwd or echo test — because the hook itself can't be located (ENOENT) before the user's actual command is even considered. There is no recovery path from inside the session: a corrective cd also goes through the broken hook and is rejected the same way.

Hook configuration (reproduces the issue)

{
  "type": "command",
  "command": "python3 scripts/harness_health_dashboard/guard-deploy-commands.py",
  "timeout": 10,
  "statusMessage": "デプロイ直叩きチェック"
}

(registered under PreToolUse for the Bash matcher in .claude/settings.json, at repo root)

Steps to reproduce

  1. In a repo with the above hook configured (relative path, no $CLAUDE_PROJECT_DIR prefix), start a session at the repo root.
  2. Run any Bash command that includes cd some/subdirectory && ....
  3. Run any further Bash command (even something unrelated and read-only, e.g. pwd).

Observed behavior

Every Bash call from step 3 onward fails before executing, with:

PreToolUse:Bash hook error: [python3 scripts/harness_health_dashboard/guard-deploy-commands.py]: /opt/homebrew/.../python3.14: can't open file '<repo_root>/some/subdirectory/scripts/harness_health_dashboard/guard-deploy-commands.py': [Errno 2] No such file or directory

The hook script does exist, just at <repo_root>/scripts/harness_health_dashboard/guard-deploy-commands.py — resolution is happening against the Bash tool's current (now-drifted) working directory rather than the project root. This reproduced consistently across multiple attempts within the same session, and also reproduced when delegating to a fresh (non-worktree-isolated) subagent — the subagent inherited the same broken state. Spawning a subagent with isolation: "worktree" was the only way to get a working Bash tool again (fresh cwd), which strongly suggests the hook's relative-path resolution is tied to Bash tool cwd state that is shared/inherited rather than reset per subagent by default.

Expected behavior

Ideally two independent things:

  1. Hook commands with a relative path should resolve against a stable root (e.g. $CLAUDE_PROJECT_DIR, matching how relative hook paths are described/expected to work elsewhere), not against the Bash tool's mutable, cd-able working directory. This would prevent the drift from happening at all.
  2. A hook that fails to even launch (ENOENT / spawn failure) should not be able to permanently deadlock the Bash tool for the rest of the session. At minimum, a cd back to a known-good directory should still be able to execute so the user/agent can self-correct. Silently failing open with a clear warning (rather than hard-blocking every future command) would also avoid a single misconfigured hook taking down the entire tool for the session.

Impact

Once triggered, this is unrecoverable without ending the session (a fresh session/turn appears to reset the tracked cwd and un-break things temporarily, until the next cd outside the hook's implicit root triggers it again). For an agent mid-task, this silently kills all further Bash-based work with no way to self-heal, and the only workaround found was delegating remaining work to a isolation: "worktree" subagent using only absolute paths and avoiding cd entirely.

Environment

  • Claude Code version: 2.1.252
  • Platform: macOS (Darwin 25.6.0)

View original on GitHub ↗