[BUG] PostToolUse hook JSON output not processed by Claude Code

Status Closed — not planned
Maintainer reply ✓ Yes — dicksontsai
Activity 12 comments · opened Jul 19, 2025 · closed Jan 6, 2026
💡 Likely answer: A maintainer (dicksontsai, collaborator) responded on this thread — see the highlighted reply below.

Environment

  • Platform (select one):
  • [x] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [ ] Other:
  • Claude CLI version: 1.0.56 (Claude Code)
  • Operating System: macOS 15.5 (Darwin 24.5.0)
  • Terminal: Ghostty.app

Bug Description

PostToolUse hooks that output JSON in the documented format are not being processed by Claude Code. The hook correctly outputs JSON with exit code 0, but Claude Code does not make this data available to the AI assistant as documented.

Steps to Reproduce

  1. Create a PostToolUse hook that outputs JSON in the documented format:

``json
{"reason": "Hook feedback message"}
``

  1. Configure the hook in .claude/settings.local.json:

``json
{
"hooks": {
"postToolUse": {
"Bash": "python3 /path/to/hook_script.py"
}
}
}
``

  1. Execute a Bash command that triggers the hook (e.g., ls)
  2. Hook executes successfully with exit code 0 and outputs the JSON
  3. Claude Code does not receive/process the JSON output

Expected Behavior

According to the hooks documentation, when a PostToolUse hook returns exit code 0 with JSON output, Claude Code should process that JSON. The AI assistant should have access to the hook's output data.

Actual Behavior

  • Hook executes correctly (verified via diagnostic logging)
  • Hook outputs proper JSON format (verified by capturing stdout)
  • Hook exits with code 0 (success)
  • Claude Code does not process the JSON output
  • The AI assistant never receives the hook data

Additional Context

This issue has been present since at least version 1.0.44. The hook implementation follows the documented specification exactly:

# Hook outputs JSON and exits with code 0
output = {"reason": "[KSI ⚡3]"}
print(json.dumps(output), flush=True)
sys.exit(0)

Diagnostic logs confirm the hook is working:

2025-07-19T10:30:59.565767 - JSON feedback output: [KSI ⚡3]

But Claude Code is not processing this output as documented. This prevents hooks from providing feedback to the AI assistant during tool execution.

Related Issues:

  • #2814 documents other hook system issues
  • Hook output visibility is only one part of the broader hooks implementation problems

Workaround:
Currently forced to write diagnostic data to external log files since the documented JSON communication method doesn't work.

View original on GitHub ↗

12 Comments

dicksontsai collaborator · 1 year ago

A bit counterintuitive, but currently, you also need to specify "decision": "block" in your PostToolUse hook for Claude Code to show "reason" to Claude. See https://docs.anthropic.com/en/docs/claude-code/hooks#posttooluse-decision-control

It might make sense to always show "reason" for PostToolUse hooks, so I'll leave this issue open.

durapensa · 1 year ago

Thank you @dicksontsai for the clarification! Adding "decision": "block" to our PostToolUse hook output resolved the issue - the reason field is now properly displayed to Claude.

I agree this behavior is counterintuitive. For future users, if you choose not to always show "reason" for PostToolUse hooks, it might help to clarify the documentation at https://docs.anthropic.com/en/docs/claude-code/hooks#posttooluse-decision-control with something like:

Note: To display a reason field to Claude in PostToolUse hooks, you must include "decision": "block" in your JSON response, even if you're not actually blocking the operation. This is currently required for the reason to be visible in Claude Code's interface.

Or perhaps add a cross-reference in the "PostToolUse hook response format" section that explicitly states this requirement, since developers might naturally assume that omitting decision (allowing the operation) would still show the reason field.

Thanks again for the help! The hook is working perfectly now.

durapensa · 1 year ago

There's an odd side-effect to the way Claude interprets the hook output now, as it appears to Claude as a user prompt:

⏺ Bash(cd /Users/dp/projects/ksi && git show --stat 58f9725)
  ⎿  commit 58f9725449226dab24344c09dc4955aabb606bf9
     Author: dp <durapensa@gmail.com>
     Date:   Sun Jul 20 10:33:28 2025 -0400
     … +19 lines (ctrl+r to expand)

> Bash operation feedback:
  - KSI ⚡1 🤖2

✻ Thinking…

  The user feedback indicates I have 1 pending task and 2 spawned agents. Let me look
   at the specific changes to the component_renderer.py file since that's where the
  dependency traversal logic likely lives.
dicksontsai collaborator · 1 year ago

@durapensa For that use case above, what do you want Claude's behavior to be? Could there be a way to tailor your message to inform Claude that there's nothing actionable?

durapensa · 1 year ago

@dicksontsai, to the best of my knowledge, there's no way I can format "reason" output such that it doesn't appear to Claude as a user message - this is Claude Code behavior.

Perhaps there's a way it could appear as a system message, ideally that's also visible to the user in the main (not ^r) interface?

dicksontsai collaborator · 1 year ago

Yes, that is a good feature request. I'll add it to my backlog.

durapensa · 1 year ago

The PostToolUse hook feedback has stopped appearing in Claude Code's interface, even when using the documented workaround.

Current behavior:

  • Hook executes successfully (confirmed via diagnostic logging)
  • JSON output exactly matches documented format: {"decision": "block", "reason": "KSI E:5"}
  • Output sent to stdout with flush
  • No feedback appears in Claude's interface

What we're doing:

output = {
    "decision": "block",  # As required by issue #3983
    "reason": message
}
print(json.dumps(output), flush=True)
sys.exit(0)

Previous behavior:
This exact code was working as recently as last week - the "reason" field would appear in Claude's interface as user feedback.

Impact:
Without hook visibility, Claude cannot see real-time system status updates, significantly limiting its ability to assist with development tasks that depend on understanding system state changes.

Environment:

  • Claude Code version: [latest as of 2025-01-27]
  • Hook type: PostToolUse
  • Tool: Bash

Has there been a recent change to how PostToolUse hooks are processed? The hook is definitely running and outputting the correct JSON, but the feedback is no longer visible to Claude.

dicksontsai collaborator · 1 year ago

This feature still works for me locally on v1.0.61 (the latest public version). What does your debug output look like?

echo_something.py

import json
import sys

output = {
    "decision": "block",  # As required by issue #3983
    "reason": "Now tell me a joke"
}
print(json.dumps(output), flush=True)
sys.exit(0)

.claude/settings.local.json

  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 $CLAUDE_PROJECT_DIR/echo_something.py"
          }
        ]
      }
    ]
  }

Transcript:

> Run a simple bash command to echo something

⏺ I'll run a simple echo command for you.

⏺ Bash(echo "Hello from the command line!")
  ⎿  Hello from the command line!

> Bash operation feedback:
  - Now tell me a joke

⏺ Why don't programmers like nature?

  It has too many bugs! 🐛

Transcript with debug:

> Run a simple bash command to echo something
[DEBUG] Stream started - received first chunk

⏺ I'll run a simple echo command for you.
[DEBUG] executePreToolHooks called for tool: Bash
[DEBUG] Executing hooks for PreToolUse:Bash
[DEBUG] Getting matching hook commands for PreToolUse with query: Bash
[DEBUG] Found 0 hook matchers in settings
[DEBUG] Matched 0 unique hooks for query "Bash" (0 before deduplication)
[DEBUG] Found 0 hook commands to execute
[DEBUG] Bash tool invoked with command: echo "Hello from the terminal!"
[DEBUG] Bash tool description: Prints a simple greeting message
[DEBUG] Executing hooks for PostToolUse:Bash
[DEBUG] Getting matching hook commands for PostToolUse with query: Bash
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 unique hooks for query "Bash" (1 before deduplication)
[DEBUG] Found 1 hook commands to execute

⏺ Bash(echo "Hello from the terminal!")
  ⎿  Hello from the terminal!
[DEBUG] Executing hook command: python3 $CLAUDE_PROJECT_DIR/echo_something.py with timeout 60000ms
[DEBUG] Hook command completed with status 0: python3 $CLAUDE_PROJECT_DIR/echo_something.py
[DEBUG] Hook stdout: {"decision": "block", "reason": "Now tell me a joke"}
...
[DEBUG] Successfully parsed and validated hook JSON output
[DEBUG] Parsed JSON output from hook: {"decision":"block","reason":"Now tell me a joke"}
[DEBUG] Processed hook result: {"permissionBehavior":"deny","blockingError":{"blockingError":"Now tell me a joke","command":"python3 $CLAUDE_PROJECT_DIR/echo_something.py"},"hookPermissionDecisionReason":"Now tell me a joke"}

> Bash operation feedback:
  - Now tell me a joke
[DEBUG] Stream started - received first chunk

⏺ Why do programmers prefer dark mode?

  Because light attracts bugs!
durapensa · 1 year ago

@dicksontsai thanks for the help! I was on vacation last week.

the recent 'PostToolUse hook feedback has stopped appearing' was caused by my hook leaking stdout logging!

I believe I've also cleaned up some [indeterminate] hook output that was triggering:

⎿ API Error: 400
    {"type":"error","error":{"type":"invalid_request_error","message":"messages.220:
    `tool_use` ids were found without `tool_result` blocks immediately after:
    toolu_01AckyS67x5smAvGPd1khm9P. Each `tool_use` block must have a corresponding
    `tool_result` block in the next message."}}
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.

github-actions[bot] · 7 months ago

This issue has been automatically closed due to 60 days of inactivity. If you're still experiencing this issue, please open a new issue with updated information.

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