[BUG] Stop hook exit code 2 downgraded to non-blocking when stderr contains unrelated 'no such file'
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?
A Stop hook that exits with code 2 (blocking) is silently downgraded to non-blocking when stderr happens to contain the text "no such file" — even when that text comes from test output, not from a missing hook script.
The hook runner has a special-case heuristic for Stop/SubagentStop/TaskCompleted/TeammateIdle events: if exit code is 2, stdout is empty, and stderr matches /no such file|can't open/i, it assumes the hook script itself is missing and treats the failure as non-blocking. The problem is the regex matches anywhere in stderr, so test output containing e.g. FileNotFoundError: [Errno 2] No such file or directory: '/dev/uinput' triggers the false positive.
The relevant code path (decompiled from v2.1.144 binary):
if (status === 2
&& (event === "Stop" || event === "SubagentStop" || event === "TaskCompleted" || event === "TeammateIdle")
&& !stdout.trim()
&& /no such file|can't open/i.test(stderr)) {
// Treats as non-blocking "hook script appears to be missing"
}
What Should Happen?
Exit code 2 from a Stop hook should block, as documented. The "missing script" heuristic should not match arbitrary content in stderr — it should be scoped to the hook script path itself (e.g., matching only the command that was invoked, not the full stderr output).
Error Messages/Logs
Stop hook error: Hook script appears to be missing — "${CLAUDE_PLUGIN_ROOT}/hooks/python-pytest-passes.sh" exited 2 with: Error: pytest tests are failing!
[... full pytest output including ...]
FileNotFoundError: [Errno 2] No such file or directory: '/dev/uinput'
[...]
Please fix the failing tests before proceeding.. Treating as non-blocking instead of re-prompting. If this is a plugin hook, check the plugin install (run /plugin).
Steps to Reproduce
- Create a Stop hook that runs a test suite and exits with code 2 on failure, writing output to stderr (stdout empty)
- Have one of the tests fail with a
FileNotFoundErroror any error containing "no such file" in the traceback - Observe that the hook is treated as non-blocking ("Hook script appears to be missing") instead of blocking the stop
Minimal reproduction — a Stop hook script that does:
#!/bin/bash
echo "FileNotFoundError: No such file or directory: '/dev/foo'" >&2
exit 2
This will be treated as non-blocking despite exit code 2.
Claude Model
Opus
Is this a regression?
I don't know
Claude Code Version
2.1.144 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Xterm
Additional Information
The hook in question is a pytest Stop hook from a plugin. It exits 2 when tests fail and writes the pytest output to stderr (as is conventional for error reporting). The false positive occurs because a containerized e2e test fails with a FileNotFoundError for /dev/uinput, and that traceback text in stderr matches the overly broad regex.
Suggested fix: anchor the regex to the hook command/path itself rather than matching anywhere in stderr. For example, check if stderr starts with or only contains a shell "no such file" error for the hook script path, rather than searching the full stderr output.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗