Feature Request: Silent/non-error display mode for Stop hook block decisions

Resolved 💬 2 comments Opened Apr 4, 2026 by Bizuayeu Closed Apr 4, 2026

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)

View original on GitHub ↗

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