[BUG] PostToolUse hook not invoked when editing files outside project CWD (path-join bug in Go binary)

Status Fixed / completed
Reported on v2.1.183
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Jun 19, 2026 · closed Aug 20, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

---
Title: PostToolUse hook not invoked when editing files outside project CWD (path-join bug in Go
binary)

Version: 2.1.183 (macOS, native Mach-O x86_64)

Reproduce:

  1. Start Claude Code in project /Users/les/Projects/mahavishnu
  2. Edit any file outside that directory (e.g. /Users/les/Projects/css-mcp/css_mcp/analyzer.py)
  3. See: PostToolUse:Edit hook additional context: open

/Users/les/Projects/mahavishnu/Users/les/Projects/css-mcp/css_mcp/analyzer.py: no such file or
directory

Evidence the hook never runs: Added sys.stderr.write(...) as the first line after the hook reads
file_path — no output ever appeared.

Root cause (observed): Claude Code constructs the path as:
filepath.Join(cwd, strings.TrimPrefix(absoluteFilePath, "/"))
which produces <cwd>/<absolute-path-without-leading-slash> instead of using the absolute path
directly. The os.Open() of this invalid path fails with Go's *os.PathError, the hook spawn is
aborted, and the error is surfaced as "hook additional context."

Impact: PostToolUse hooks never fire for any file outside the active project directory, silently
skipping all hook side-effects (reflection storage, code indexing, etc.).

Workaround: None from user config; binary-level fix required.

---

What Should Happen?

see above

Error Messages/Logs

Steps to Reproduce

see above

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.183

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

_No response_

View original on GitHub ↗

3 Comments

kratocz · 3 months ago

Confirming this is still present on 2.1.205 (macOS), and adding two repro cases the original report doesn't cover — same root cause (filepath.Join(cwd, strings.TrimPrefix(absoluteFilePath, "/"))), broader trigger surface.

1. It also fires for a file _inside_ the project, when CWD is a git worktree nested under the main checkout.

The title frames this as "files _outside_ project CWD", but the doubled-path bug also hits files that are genuinely inside the working directory when there's a project-root vs. worktree-root mismatch. Setup:

  • main checkout: /Users/me/proj/myrepo (holds the real .git directory)
  • worktree: /Users/me/proj/myrepo/.worktrees/wt-1 (its .git is a file: gitdir: …/myrepo/.git/worktrees/wt-1)

Editing .../wt-1/.github/workflows/foo.yml (a file inside the worktree/CWD) produces:

PostToolUse:Edit hook additional context: open /Users/me/proj/myrepo/.worktrees/wt-1/Users/me/proj/myrepo/.worktrees/wt-1/.github/workflows/foo.yml: no such file or directory

The worktree is nested under the main checkout, so the project-root walk finds the main repo's .git while the file's absolute path is under the worktree — the same TrimPrefix+Join branch fires and doubles the path. A non-nested, non-worktree checkout (where cwd == project root) sidesteps it, which is probably why it reads as "outside CWD only".

2. Same bug surfaces via the built-in dependency/lockfile scanner (PreToolUse:Bash), not just PostToolUse:Edit.

With no hooks configured anywhere, Bash commands still emit:

PreToolUse:Bash hook additional context: open <doubled-path>/package-lock.json: no such file or directory

for lockfiles the built-in scanner probes (package-lock.json, Gemfile.lock, etc.). Same message shape, same doubled path, different built-in caller — so the fix at filepath.Join(cwd, TrimPrefix(absPath,"/")) would clear both surfaces at once.

Impact confirmation: cosmetic in the sense that the Edit/Bash still succeeds, but the hook's/scanner's side-effect is silently skipped for that path, and the ENOENT string gets injected into the model's context as "additional context" on every such call — noisy across a long session.

Suggested guard: only Join(cwd, x) when !filepath.IsAbs(x); otherwise use the absolute path directly (or filepath.Rel(projectRoot, absPath) if a project-relative path is actually needed).

bcherny collaborator · 1 month ago

Tried to reproduce on 2.1.233 (Linux): registered a PostToolUse hook with an Edit matcher, then had Claude edit a file outside the project directory. The hook fired, the JSON on its stdin contained the correct absolute file_path (not joined with the project directory), and the edit applied — so Claude Code is passing the path through unmodified, and I couldn't find any release between 2.1.183 and now that changed this.

Two observations that point at the hook command itself rather than Claude Code:

  1. The line PostToolUse:Edit hook additional context: open …: no such file or directory is Claude Code relaying your hook's own output. That error string is the format a Go program produces — Claude Code isn't written in Go. Hooks run with their working directory set to the project directory, so if any Go binary in your hook chain joins its cwd with the (already absolute) file_path, you get exactly the doubled path you saw.
  2. sys.stderr.write(...) not appearing isn't evidence the hook didn't run: hook stderr (on exit code 0) goes to the debug log only, not the transcript. Check with claude --debug.

Could you check the hook command chain (especially any Go wrapper/launcher) for path joining, and use file_path from stdin as-is? Docs: https://code.claude.com/docs/en/hooks. If you still hit this with file_path used verbatim, please share the hook config from your settings and we'll take another look.

🤖 Generated with Claude Code

github-actions[bot] · 4 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.