PreToolUse hooks fail when multiple hooks match the same tool (stdin contention)
Resolved 💬 2 comments Opened Apr 2, 2026 by arnav5543 Closed May 11, 2026
Bug Description
When multiple PreToolUse hooks match the same tool (e.g., Edit), only one hook appears to receive the stdin JSON input. Other matching hooks get empty stdin, causing them to silently pass (exit 0) instead of executing their logic.
Steps to Reproduce
- Define a
PreToolUsehook in.claude/settings.jsonwith matcherEdit|Writethat reads stdin viacatand usesjqto extracttool_input.file_path - Have a plugin enabled that also defines a
PreToolUsehook matchingEdit|Write(e.g., thesecurity-guidanceplugin) - Trigger an
Edittool call
Expected Behavior
Each hook should receive its own copy of the stdin JSON input, allowing all matching hooks to evaluate independently.
Actual Behavior
The hook defined in .claude/settings.json receives empty stdin. The jq extraction returns an empty string, the guard condition doesn't match, and the hook exits 0 (allow) — silently bypassing the intended block.
Debugging Done
- The hook logic works correctly when tested manually in bash with piped JSON input
jqis confirmed installed and functional- Disabling one plugin's PreToolUse hook did not resolve the issue (a second plugin also had a PreToolUse hook consuming stdin)
- The hook is correctly defined in a valid settings location (project-level
.claude/settings.json)
Environment
- macOS (Darwin 23.6.0)
- Claude Code CLI
- Hooks defined in project-level
.claude/settings.json - Multiple plugins enabled that also define
PreToolUsehooks (security-guidance, hookify)
Example Hook
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "INPUT=$(cat); FILE=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // .tool_input.path // \"\"'); if echo \"$FILE\" | grep -qE 'package\\.json|\\.env|CLAUDE\\.md'; then echo \"BLOCKED: Cannot modify $FILE\" >&2; exit 2; fi; exit 0"
}
]
}
]
}
}This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗