[Bug] Stop hooks with exit code 2 fail to continue when installed via plugins

Status Fixed / completed
Maintainer reply None cached
Activity 9 comments · opened Oct 27, 2025 · closed Nov 2, 2025

Summary

Stop hooks that return exit code 2 (to force agent continuation) work correctly when installed in .claude/hooks/ but fail when installed via the plugin system, displaying ⏺ Stop hook prevented continuation and halting instead of continuing.

Expected Behavior

When a Stop hook:

  1. Exits with code 2
  2. Outputs JSON to stderr containing "decision": "block" and a "reason" field

Claude Code should:

  1. Parse the JSON from stderr
  2. Extract the reason field
  3. Feed the reason to Claude as continuation instructions
  4. Resume agent execution

This is the documented behavior and works correctly for hooks in .claude/hooks/.

Actual Behavior (Plugin Hooks Only)

When the same hook is installed via plugins:

  1. Hook executes and returns exit code 2 with JSON to stderr
  2. Claude Code displays: ⏺ Stop hook prevented continuation
  3. Agent execution halts instead of continuing

Reproduction Evidence

Test script that proves identical behavior from both hook locations:

\\\`bash

Create test transcript with reflexive agreement pattern

cat > /tmp/test_transcript.json << 'INNER_EOF'
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"You're wrong"}]}}
{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"You're absolutely right."}]}}
INNER_EOF

Test direct hook

echo '{"session_id":"test","stop_hook_active":false,"transcript_path":"/tmp/test_transcript.json"}' | \
~/.claude/hooks/entrypoints/stop.rb 2>&1
echo "Exit: $?"

Test plugin hook

echo '{"session_id":"test","stop_hook_active":false,"transcript_path":"/tmp/test_transcript.json"}' | \
~/.claude/plugins/marketplaces/simpleclaude/plugins/sc-hooks/hooks/entrypoints/stop.rb 2>&1
echo "Exit: $?"
\\\`

Both produce identical output:
\\\json
{"continue":true,"stopReason":"","suppressOutput":false,"decision":"block","reason":"I notice I just used a reflexive agreement phrase. Let me provide a more substantive response:\n\nInstead of simply agreeing, let me analyze your point with specific technical reasoning, consider potential edge cases or alternative approaches, and offer constructive insights that build collaboratively on your observation."}
\
\\
Exit code: 2 (both)

Root Cause

The hook implementation is correct. Claude Code has different code paths for:

  • Direct hooks (.claude/hooks/): Exit 2 → parse stderr → continue ✓
  • Plugin hooks (plugins/): Exit 2 → show error message → halt ✗

The plugin execution path appears to:

  • Not read stderr properly, OR
  • Not parse the JSON from stderr, OR
  • Not extract/use the reason field

Environment

  • Platform: macOS
  • Claude Code Version: 0.3.10
  • Hook Implementation: Uses claude_hooks Ruby gem v0.7.0
  • Plugin: SimpleClaude (sc-hooks)

Workaround

Moving the Stop hook from plugins/sc-hooks/hooks/ to .claude/hooks/ makes it work correctly.

Related

Original discussion: https://github.com/gabriel-dehan/claude_hooks/issues/11

View original on GitHub ↗

9 Comments

secustor · 10 months ago

I can confirm this behaviour with 2.0.29 on MacOS.

kylesnowschwartz · 10 months ago

I'm so sad that this isn't getting more attention. This is a very weird quirk of Claude-Code's internals that is making me not want to use a plugin for my hook system :(

Why would plugin hooks not respect the exit codes but user-level hook would?

kylesnowschwartz · 10 months ago
secustor · 10 months ago

I think this should be reopened as these are different issues with the same root cause.

The linked ticket is about the JSON output, but the behavior is also showing for exit code 2 without any JSON output

kylesnowschwartz · 10 months ago

Are you able to reproduce it @secustor ? If so add your reproduction steps and I'll re-open. I was unable to reproduce it today.

secustor · 10 months ago

Yes, I have used your reproduction as bases ( thanks for that ) to show the behaviour

https://github.com/secustor/claude-code-plugin-hook-reproduction#

kylesnowschwartz · 9 months ago

Thanks @secustor I agree it's the same bug. I think https://github.com/anthropics/claude-code/issues/10875 demonstrates and explains it better though - so please upvote.

secustor · 9 months ago

My use case does not involve JSON output, but it seems like this has been identified and confirmed.

https://github.com/anthropics/claude-code/issues/10875#issuecomment-3488536561

So it should be fine to keep it closed.

github-actions[bot] · 9 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.