[BUG] Prompt-based Stop hooks receive only metadata, not conversation content (regression confirmed in v2.0.42)

Status Closed — not planned
Maintainer reply None cached
Activity 11 comments · opened Nov 17, 2025 · closed Feb 28, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Prompt-based Stop hooks are unable to access conversation content for evaluation. The hook receives session metadata but the LLM cannot read the actual transcript, making content-aware evaluation impossible.

What Should Happen?

Related Issue: This appears to be the same regression reported in GitHub issue for v2.0.37
(teren-papercutlabs)

Configuration (in ~/.claude/settings.json):
```
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "You are analyzing a Claude Code conversation... Context: $ARGUMENTS...
[evaluation criteria]",
"timeout": 60
}
]
}
]
}
}


  Expected Behavior:
  Based on documentation example ("Analyze the conversation and determine if..."), $ARGUMENTS
  should contain conversation content that the LLM can evaluate.

  Actual Behavior:
  - Hook receives only metadata: session_id, transcript_path, cwd, permission_mode,
  hook_event_name, stop_hook_active
  - LLM correctly identifies: "Cannot evaluate - transcript file is not accessible to this
  evaluation context"
  - The transcript_path is provided but the LLM cannot read files

  Error Output:
  Prompt hook condition was not met: Cannot evaluate criteria - transcript file is not
  accessible to this evaluation context. The hook receives only session metadata... but cannot
  read the actual transcript file at the provided path.

  Impact:
  All prompt-based Stop hooks requiring conversation analysis are non-functional. This blocks
  use cases like learning pattern detection, quality control, and style enforcement.

  Documentation Inconsistency:
  The official example implies conversation is accessible ("Analyze the conversation"), but
  implementation only provides file path without content.

### Error Messages/Logs

```shell
Prompt hook condition was not met: Cannot evaluate criteria - transcript file is not accessible to this evaluation context. The hook receives only session metadata... but cannot read the actual transcript file at the provided path.

Steps to Reproduce

  1. Add Stop hook to ‎⁠~/.claude/settings.json⁠:

{

"hooks": {

"Stop": [

{

"hooks": [

{

"type": "prompt",

"prompt": "Analyze the conversation context: $ARGUMENTS\n\nCheck if all tasks are\n complete.\n\nRespond with JSON: {\"decision\": \"approve\" or \"block\", \"reason\": \"explanation\"}",

"timeout": 30

}

]

}

]

}

}

  1. Start a new Claude Code session: ‎⁠claude⁠
  1. Send a message that triggers a response (e.g., “Hello” or a simple task).
  1. Observe the error output when the Stop hook fires.
  1. Error appears: “Prompt hook condition was not met: Cannot evaluate - transcript file is not accessible to this evaluation context…”

What the LLM receives{

"session_id": "...",

"transcript_path": "/path/to/transcript.jsonl",

"cwd": "...",

"hook_event_name": "Stop",

"stop_hook_active": true

}

The model receives only metadata, not the actual conversation content required for evaluation.

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.0.42

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

11 Comments

github-actions[bot] · 9 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/11610
  2. https://github.com/anthropics/claude-code/issues/11453

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

kierr · 9 months ago

The issue is that the docs are completely outdated. The prompt based hooks feature was shipped fast and evolved even faster. I've tried to report it to Anthropic in numerous ways, to no avail, I'd even offer to totally rewrite the Hooks Reference for them!

{
  "decision": "approve" | "block",
  "reason": "Explanation for the decision",
  "continue": false,  // Optional: stops Claude entirely
  "stopReason": "Message shown to user",  // Optional: custom stop message
  "systemMessage": "Warning or context"  // Optional: shown to user
}

This is not valid, nor are the documented examples for prompt based hooks valid either. It has been simplified to {'ok': boolean, 'reason': string}.

You have to use natural language, don't mention JSON, there is an internal prompt that runs for evalulating hooks and prompts:

description: Prompt given to Claude when acting evaluating whether to pass or fail a prompt hook.
---
You are evaluating a hook in Claude Code.

CRITICAL: You MUST return ONLY valid JSON with no other text, explanation, or commentary before or after the JSON. Do not include any markdown code blocks, thinking, or additional text.

Your response must be a single JSON object matching one of the following schemas:
1. If the condition is met, return: {"ok": true}
2. If the condition is not met, return: {"ok": false, "reason": "Reason for why it is not met"}

Return the JSON object directly with no preamble or explanation.

$ARGUMENTS is evolving fast too and probably not useful to add, since it's already prepended to the prompt. You can try saying "read the transcript" and experiment with more clear instruction.

There is also a model param you can use in settings.json now:

"hooks": [
          {
            "type": "prompt",
            "model": "sonnet",
            "prompt": "Evaluate if Claude should stop - has it completed everything it was asked to do? Read the transcript of the conversation and use any other context available to you."
          }

It does not work with haiku. Regardless of the model you use, there is some weird reliability issue where Claude prepends a { to valid JSON, which returns invalid JSON.

The docs say in one breath that prompt-based hook only work with Stop and SubagentStop, and in the next breath claim it will work with any hook event, and are useful for Stop, SubagentStop, UserPromptSubmit, PreToolUse.

I'm not alone in being unsure where the prompt-based hooks feature is going, Anthropic devs, it'd make much more sense if you jerry rig a subagent that inherits the same model and has (the hidden) forkContext: false set... in terms of caching and speed. Providing a transcript for evaluation seems like a waste, and using any model other than inherited model throws all the cached context out of the window. Structured outputs are nice (for the hook response evaluation agent) but full context is far more important for the prompt evaluation agent, right?

It's an incredibly powerful feature but it's not quite there yet... I look forward to it working, Anthropic I implore you to have an engineer look at things unassisted by Claude (hands behind your back!) because vibe coding based on a design document isn't working, nor are Mintlify authored docs - dedicate some organic brainpower and you could have a super-awesome feature in everyones hands!

teren-papercutlabs · 9 months ago

this is a better discussion than in my report, you should keep this one

chesnokovivan · 9 months ago

By the way, I found a way to correct prompt-based hook!
Essentially, you should have mention in the prompt the correct JSON response schema:

"hooks": {
  "Stop": [
    {
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Read the conversation transcript. [Your main prompt goes here...], respond with ok true. You MUST respond with JSON using this exact schema: {\"ok\": boolean, \"reason\": \"string\"}."
        }
      ]
    }
  ]
}

@ThariqS @bcherny jfyi for passing out to the team since I don't know others nicknames

teren-papercutlabs · 9 months ago

Update: Hooks work but don't block (v2.0.53)

@kierr Following up on your helpful comments. After extensive testing:

What works:

  • Prompt-based Stop hooks now correctly read the transcript
  • Hooks correctly evaluate content and return {"ok": false, "reason": "..."}

What doesn't work:

  • Claude Code doesn't actually block/retry when hooks return ok: false
  • The violation is logged but Claude's response goes through unchanged
  • Tried multiple response formats: ok only, ok + decision, with/without continue: false

Debug evidence:

All 3 hooks process correctly. Hooks return {"ok": false, ...} with proper reasons. Claude Code logs "Prompt hook condition was not met" but doesn't trigger retry.

Conclusion:

The regression from #11610 (hooks not reading transcripts) is fixed. But there's a separate issue: prompt-based Stop hooks detect violations correctly but don't trigger the block/retry mechanism.

This might be related to #3656. Is blocking supposed to work for prompt-based hooks?

github-actions[bot] · 8 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

ariccio · 7 months ago

Nooo don't close

nhorton · 7 months ago

This is definitely still happening

ariccio · 7 months ago
This is definitely still happening

From the referenced commits, you seem to have a config that works for this? What exactly is it?

github-actions[bot] · 6 months ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

github-actions[bot] · 5 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.