Feature Request: Silent/non-error display mode for Stop hook block decisions
Feature Request: Silent/non-error display mode for Stop hook block decisions
Problem
When a Stop hook returns {"decision": "block", "reason": "...", "systemMessage": "..."}, Claude Code displays the reason as "Stop hook error" in the terminal. This is misleading for plugins that use block + systemMessage for legitimate, non-error purposes.
For example, the EmotionPulse plugin uses a Stop hook to inject a systemMessage that instructs the agent to self-evaluate its emotional state. This is normal operation, not an error — but users see:
Stop hook error: EmotionPulse: evaluating emotions
This causes confusion and alarm for end users.
Why block is required
We tested all available alternatives to avoid the "error" display:
| Approach | Error display | systemMessage injected |
|---|---|---|
| {"decision": "block", "reason": "...", "systemMessage": "..."} | Yes | Yes |
| {"decision": "approve", "systemMessage": "..."} | No | No |
| {"systemMessage": "..."} (no decision field) | No | No |
| async: true hook | No | No (fire-and-forget) |
Conclusion: For Stop hooks, block is the only way to inject a systemMessage into the conversation. Since the turn ends on approve, the systemMessage has nowhere to go. This means any plugin that needs to inject instructions at the end of a turn must use block, and must accept the "error" label.
Proposed solution
Add a field to suppress or customize the "error" display for block decisions. For example:
Option A: silent flag
{
"decision": "block",
"silent": true,
"systemMessage": "..."
}
Block with systemMessage injection, but no terminal display.
Option B: severity / display level
{
"decision": "block",
"severity": "info",
"reason": "EmotionPulse: reflecting",
"systemMessage": "..."
}
Display with an appropriate label ("info" / "warning" / "error") instead of always "error".
Option C: Support systemMessage with approve
{
"decision": "approve",
"systemMessage": "..."
}
Inject the systemMessage into the next turn's context, then end the current turn normally. This would enable a 1-turn-delayed injection without blocking.
Use case
Plugins that use Stop hooks for self-reflective or observational purposes (emotion tracking, quality checks, journaling) need block + systemMessage but have no reason to alert the user with an "error" label. The current behavior makes these plugins appear broken to end users.
Environment
- Claude Code v2.1.92
- Windows 11
- Plugin: EmotionPulse (Stop hook + statusline)
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗