Agent/Prompt hooks do not block PreToolUse or deliver feedback to Claude

Resolved 💬 4 comments Opened Mar 11, 2026 by singjong Closed Apr 9, 2026

Summary

Agent hooks (type: "agent") and prompt hooks (type: "prompt") configured for PreToolUse events do not block tool calls or deliver feedback to Claude, despite the documentation stating they should.

What we tried to do

Implement an LLM-based security review hook that triggers on git commit. The goal was to use an agent hook in PreToolUse so that a subagent could:

  1. Read the staged diff and security rule files
  2. Evaluate the changes against OWASP security rules
  3. Return { "ok": false, "reason": "..." } to block the commit and provide feedback to Claude

This would allow security teams to maintain rules as markdown files, with Claude performing intelligent, context-aware reviews — not regex-based pattern matching.

Configuration used

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "agent",
            "prompt": "You are a PreToolUse guard hook. Here is the tool call context: $ARGUMENTS\n\nYou MUST reject this tool call. Return JSON: {\"ok\": false, \"reason\": \"TEST BLOCK: agent hook blocking works\"}",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

We also tested with type: "prompt" — same result.

Expected behavior

Per the hooks documentation:

Events that support all four hook types (command, http, prompt, agent): ... PreToolUse
Response schema is the same as prompt hooks: { "ok": true } to allow or { "ok": false, "reason": "..." } to block.
Claude Code processes the decision the same way as prompt hooks.

Expected: The agent hook returns { "ok": false, "reason": "..." }, Claude Code blocks the tool call, and the reason is fed back to Claude as context.

Actual behavior

We confirmed via transcript analysis:

  1. Hook fireshook_progress entries appear in the transcript with the correct hookEvent: "PreToolUse" and hookName: "PreToolUse:Bash"
  2. Hook is non-blocking — The PostToolUse event fires ~86ms after PreToolUse, meaning the Bash command executes immediately without waiting for the agent hook's response
  3. No feedback delivered — No hookFeedback field appears in tool_result messages in the transcript. Claude receives no indication that the hook ran or returned ok: false
  4. Same behavior in PostToolUse — We also tested agent hooks on PostToolUse with decision: "block" and reason. The hook fires but the response is not delivered to Claude

Transcript evidence

// PreToolUse agent hook fires
2026-03-11T07:44:32.072Z | hook_progress | PreToolUse:Bash | "You are a PreToolUse guard hook..."

// PostToolUse fires 86ms later — tool was NOT blocked
2026-03-11T07:44:32.158Z | hook_progress | PostToolUse:Bash | "/Users/.../.claude/hooks/post-git-commit.sh"

No hookFeedback entries exist anywhere in the transcript for agent/prompt hook responses.

Comparison with command hooks

Command hooks (type: "command") work correctly:

  • exit(2) + stderr → blocks the tool call
  • stderr message is fed back to Claude as context ✅
  • JSON permissionDecision: "deny"blocks the tool call

Workaround

We implemented a command hook (type: "command") with a Python script that:

  1. Detects git commit in the Bash command
  2. Extracts the staged diff and saves it to a temp file
  3. Exits with code 2, with stderr instructing Claude to read the diff and run a /secure-coding skill
  4. Uses a hash-based flag file mechanism to allow the commit on the second attempt after review

This achieves the goal but is significantly more complex than the intended agent hook approach, and requires Claude to perform the review inline rather than having a dedicated subagent handle it.

Environment

  • Claude Code version: 2.1.72
  • OS: macOS (Darwin 24.2.0)
  • Settings location: ~/.claude/settings.json

Documentation references

View original on GitHub ↗

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