[Bug] Stop hooks can hang indefinitely on large JSON block responses since 2.1.78
Summary
Claude Code appears to regress in 2.1.78+ when a plugin Stop hook returns a large JSON payload on stdout.
In our case, a plugin stop hook returns {"decision":"block", ...} with a large reason body containing the next-round prompt / review content. This worked in 2.1.77, but from 2.1.78 onward Claude Code can hang indefinitely at:
running stop hooks... 1/2
Regression window
- Last known good:
2.1.77 - First known bad:
2.1.78 - Still reproducible in:
2.1.81
Platforms
Observed on:
- Windows
- macOS
We also reproduced the same blocking mechanism on Linux/WSL with a synthetic fixture, although the size threshold is higher there.
What we observe in real runs
When the bug happens:
- the plugin has already produced the review artifact (for example
round-0-review-result.md) - in some cases it has already also produced the next prompt and advanced loop state
- Claude Code stays stuck on
running stop hooks... 1/2 - the UI does not return control to the session
This makes it look like the hook is still "working", even though the business logic has already completed.
Minimal reproduction pattern
The issue is not specific to one plugin implementation. The minimal triggering pattern is:
- A
Stophook writes a valid JSON block response to stdout - The JSON payload is large enough (tens of KiB is enough on some platforms)
- Claude Code does not return from the stop phase
Shape of the response:
{
"decision": "block",
"reason": "<large payload>",
"systemMessage": "..."
}
Controlled reproduction results
We built controlled A/B tests around the same stop-hook logic:
- Variant A: parent process does not continuously read hook stdout
- Variant B: parent process does continuously read hook stdout
Windows / macOS plugin reproduction
For the same hook logic:
- Variant A hangs
- Variant B returns quickly
Linux / WSL synthetic reproduction
With the same stop-hook structure:
- small payloads complete normally
- once the JSON payload crosses a threshold, Variant A hangs while Variant B completes quickly
This strongly suggests the hang is related to hook stdout consumption / backpressure rather than plugin business logic.
Why we believe this is a Claude Code regression
Two reasons:
- The exact same plugin workflow worked on
2.1.77 - The first bad version is
2.1.78, which introduced stop-hook-adjacent behavior changes in the release notes, including line-by-line response streaming and other hook/runtime handling changes
Important nuance
It is possible that plugins should avoid returning very large stop-hook payloads. That said, the current behavior is a hard hang with no explicit size-limit error or fallback behavior.
Even if Claude Code wants to impose a limit here, it would be much safer to:
- drain hook stdout reliably, or
- reject oversized hook responses with a clear error, rather than hanging indefinitely
Expected behavior
Claude Code should do one of the following:
- reliably consume stop-hook stdout even for large JSON payloads, or
- reject oversized stop-hook responses with a clear, deterministic error message
It should not hang indefinitely after hook business logic has already completed.
Actual behavior
Claude Code can hang indefinitely in stop-hook processing after the hook has already completed its internal work.
Notes
We separately found and fixed a plugin-side pipe issue in our own code for draining child process output from a review subprocess. That was a real bug on our side, but it does not explain this stop-hook regression window, because:
- the same hang behavior still exists at the stop-hook stdout boundary, and
- the last-known-good / first-known-bad split still points at
2.1.78
If helpful, I can provide the exact PowerShell and Linux repro scripts used for the A/B stdout-consumption tests.
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗