[BUG] PreToolUse hooks cause agent hang after ToolSearch deferred tool loading
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
- Create a plugin with a
hooks/hooks.jsoncontaining aPreToolUsehook:
``json``
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "some-script.sh",
"timeout": 5
}
]
}
]
}
}
- 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
- Send a prompt that causes the agent to call
ToolSearchon the first turn (e.g., to load theReadtool).
- The first API turn completes successfully — the agent produces thinking, text, and a
ToolSearchtool call. TheToolSearchresult is returned with the tool definition.
- 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>
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗