[BUG] PreToolUse hooks cause agent hang after ToolSearch deferred tool loading

Resolved 💬 4 comments Opened Mar 11, 2026 by elitecoder Closed Apr 8, 2026

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 PreToolUse hook in a plugin's hooks.json causes the claude CLI (claude -p) to hang indefinitely on the second API turn when the agent uses ToolSearch to load a deferred tool on the first turn. The hang occurs even when the hook's matcher (Edit|Write) does not match the tool being used (ToolSearch).

What Should Happen?

The PreToolUse hook should only evaluate for tools matching its matcher pattern (Edit|Write). The ToolSearch tool call and its result should not be affected by a non-matching PreToolUse hook.

Error Messages/Logs

Tested with 5 consecutive planner sessions — all exhibited the same hang:

| Session | Stream lines | Gatekeeper status | Notes |
|---------|-------------|-------------------|-------|
| plan_2026-03-11_025123Z | 1 | failed | Never got first response |
| plan_2026-03-11_025935Z | 1 | failed | Never got first response |
| plan_2026-03-11_031019Z | 5 | failed | Stalled after ToolSearch |
| plan_2026-03-11_032059Z | 5 | failed | Stalled after ToolSearch |
| plan_2026-03-11_033518Z | 5 | failed | Stalled after ToolSearch |

After removing the `PreToolUse` hook (keeping `PostToolUse` and `Stop` hooks unchanged):

| Session | Stream lines | Gatekeeper status | Notes |
|---------|-------------|-------------------|-------|
| plan_2026-03-11_035243Z | 26 | complete | Ran to completion |

The stream-json output for stalled sessions consistently shows:

Line 1: system/init
Line 2: assistant/thinking
Line 3: assistant/text
Line 4: assistant/tool_use: ToolSearch {"query": "select:Read", "max_results": 1}
Line 5: user/tool_result: tool_ref=Read
(hangs here — no line 6)

Steps to Reproduce

Reproduction

  1. Create a plugin with a hooks/hooks.json containing a PreToolUse hook:

``json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "some-script.sh",
"timeout": 5
}
]
}
]
}
}
``

  1. Launch claude with the plugin and an agent that uses deferred tools:

``bash
claude -p \
--dangerously-skip-permissions \
--output-format stream-json \
--plugin-dir <plugin-dir> \
--agent <agent-name> \
--model opus
``

  1. Send a prompt that causes the agent to call ToolSearch on the first turn (e.g., to load the Read tool).
  1. The first API turn completes successfully — the agent produces thinking, text, and a ToolSearch tool call. The ToolSearch result is returned with the tool definition.
  1. The second API call never returns. The process stays alive, the reader thread sees no new output, and the agent hangs indefinitely.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.72

Platform

AWS Bedrock

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Workaround

Moving the hook from PreToolUse to PostToolUse resolves the hang. The write has already happened by the time PostToolUse fires, but the agent receives feedback via stderr (exit code 2) and can correct itself.

Environment

  • claude-code: 2.1.72
  • Platform: macOS Darwin 25.3.0 (Apple Silicon)
  • Model: us.anthropic.claude-opus-4-1-20250805-v1:0
  • Plugin: Custom plugin with hooks.json (PreToolUse on Edit|Write)
  • Launch mode: claude -p --dangerously-skip-permissions --output-format stream-json --plugin-dir <dir> --agent <name>

View original on GitHub ↗

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