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

  1. Define a PreToolUse hook in .claude/settings.json with matcher Edit|Write that reads stdin via cat and uses jq to extract tool_input.file_path
  2. Have a plugin enabled that also defines a PreToolUse hook matching Edit|Write (e.g., the security-guidance plugin)
  3. Trigger an Edit tool 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
  • jq is 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 PreToolUse hooks (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"
          }
        ]
      }
    ]
  }
}

View original on GitHub ↗

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