[Feature Request] Distinguish 'blocked' hooks from 'error' hooks in UI

Resolved 💬 3 comments Opened Feb 3, 2026 by duarbdhks Closed Feb 7, 2026

Summary

When a Stop hook intentionally blocks session termination by returning a non-zero exit code, Claude Code displays it as "Stop hook error". This is misleading because intentional blocking is not an error.

Current Behavior

⏺ Ran 1 stop hook
  ⎿  Stop hook error: [ULTRAWORK #1/50] Mode active. Continue working...

The word "error" implies something went wrong, but this is intentional behavior - the hook is designed to prevent premature session termination during long-running workflows.

Expected Behavior

Distinguish between:

  1. Blocked - Hook intentionally prevented the action (non-zero exit, but by design)
  2. Error - Hook failed unexpectedly (actual error)

Suggested UI:

⏺ Ran 1 stop hook
  ⎿  Stop hook blocked: [ULTRAWORK #1/50] Mode active. Continue working...

Or perhaps:

⏺ Ran 1 stop hook
  ⎿  Stop hook intercepted: [ULTRAWORK #1/50] Mode active. Continue working...

Use Case

Plugins like oh-my-claudecode use Stop hooks to implement "persistent modes" (ultrawork, ralph) that prevent session termination until tasks are verified complete. Users see "error" and think something is broken, when it's actually working as intended.

Proposed Solution

Option A: Use different terminology based on hook's exit message pattern

  • If message starts with [BLOCK] or similar marker → display as "blocked"
  • Otherwise → display as "error"

Option B: Add a hook protocol

  • Exit code 1 = error
  • Exit code 2 = intentional block (new convention)

Option C: Let hooks specify their intent

  • Hook outputs JSON: {"type": "block", "message": "..."}

Environment

  • Claude Code version: Latest
  • OS: macOS / Linux / Windows

View original on GitHub ↗

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