Hook documentation is misleading — Stop hooks broken, prompt hooks can't inject context, capabilities undocumented per event type

Resolved 💬 4 comments Opened Mar 22, 2026 by TalkingMonkeyOz Closed Apr 21, 2026

Summary

After 3+ sessions (approx 15 hours) trying to implement hooks for enforcing behavior in Claude Code, we've found the documentation to be misleading and several hook types to be non-functional. We're filing this as constructive feedback — we'd love to be corrected if we're doing something wrong, but our testing has been thorough and the results are clear.

Environment

  • Claude Code v2.1.81, Windows 11
  • Tested with Opus, Sonnet, and Haiku models
  • Both interactive and -p (print) modes

Issue 1: Stop hooks don't work (JSON validation errors)

What we tried:

{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "prompt",
        "model": "sonnet",
        "prompt": "Evaluate if Claude should stop. Has it completed everything the user asked?"
      }]
    }]
  }
}

What happened: Persistent "JSON validation failed" errors across 3 sessions, regardless of:

  • Using model: "sonnet" vs model: "haiku"
  • Natural language prompts vs structured prompts
  • Various JSON output format instructions

Related issues: #11947, #11786, #11610 — these appear to still be unresolved as of v2.1.81.

Issue 2: type: "prompt" hooks on UserPromptSubmit can only block — they cannot inject context

What the docs suggest: That prompt hooks evaluate user input and can influence behavior.

What actually happens: A type: "prompt" hook on UserPromptSubmit can only return {"ok": true} (allow) or {"ok": false, "reason": "..."} (block the prompt entirely). When ok: false, the user sees the reason and Claude never sees the prompt at all.

What we expected: The ability to inject context or modify the prompt, similar to what type: "command" hooks can do with additionalContext or replacementPrompt.

This limitation is not documented anywhere. The docs don't distinguish which output fields are available per hook type per event type. We spent hours trying to get prompt hooks to inject context before discovering through trial and error that only type: "command" hooks support additionalContext and replacementPrompt.

Issue 3: No hooks fire in -p (print/non-interactive) mode

Test: We deployed a "block all prompts" hook:

{
  "type": "prompt",
  "prompt": "Always return {\"ok\": false, \"reason\": \"All prompts blocked\"}."
}

Result: claude -p "What is 2+2?" returned "4" — the hook never fired. This makes automated testing of hooks impossible without interactive sessions.

This is not documented.

Issue 4: Documentation doesn't distinguish capabilities per hook type per event

The hooks documentation at https://code.claude.com/docs/en/hooks presents all hook types (command, prompt, agent) and all events (UserPromptSubmit, Stop, PreToolUse, etc.) but doesn't clearly document:

  1. Which output fields work with which hook type (e.g., additionalContext only works with type: "command", not type: "prompt")
  2. Which hook types work with which events (e.g., Stop + prompt = broken)
  3. What ok: false does per event type (UserPromptSubmit blocks the prompt; Stop feeds reason back to Claude — very different behaviors)
  4. Which modes support hooks (-p mode skips them entirely)

What we'd like

  1. A capability matrix in the docs: hook type × event type × available output fields
  2. Fix or remove Stop hooks — if they don't work, document that clearly rather than leaving users to discover it through hours of debugging
  3. Document -p mode hook behavior — even a one-liner saying "hooks don't fire in print mode" would save significant time
  4. Consider adding replacementPrompt support to prompt hooks — currently only command hooks can modify prompts, which requires writing a script. Prompt hooks being limited to yes/no gatekeeping severely limits their utility.

Our workaround

We use a type: "command" Python script on UserPromptSubmit that injects behavioral rules via additionalContext. This works but is essentially "persuasion" — Claude sees the rules but can choose to ignore them, especially at large context sizes. We'd prefer actual enforcement hooks but the current implementation doesn't support this reliably.

Acknowledgment

We appreciate Claude Code — it's genuinely powerful and we use it extensively across 10+ projects. This feedback comes from wanting it to be better, not from frustration alone. Happy to provide logs, test scripts, or further details if helpful.

View original on GitHub ↗

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