[BUG] Hookify plugin not passing blocking messages to Claude (missing permissionDecisionReason)

Status Fixed / completed
Maintainer reply None cached
Activity 8 comments · opened Nov 26, 2025 · closed Jan 16, 2026

Bug Description

The hookify plugin blocks tool calls correctly but the detailed blocking message is only shown to the user, not passed to Claude. This prevents Claude from understanding why a tool was blocked and how to fix it.

Root Cause

In plugins/hookify/core/rule_engine.py (lines 72-79), when blocking a PreToolUse, hookify returns:

return {
    "hookSpecificOutput": {
        "hookEventName": hook_event,
        "permissionDecision": "deny"
    },
    "systemMessage": combined_message
}

Per the hooks documentation, systemMessage is shown to the user, but permissionDecisionReason is what gets passed to Claude when permissionDecision is "deny".

Expected Behavior

Claude should receive the detailed blocking message (e.g., "Use uv run pytest instead of pytest") so it can correct its behavior.

Actual Behavior

Claude only receives a generic "Hook PreToolUse:Bash denied this tool" message without the helpful guidance from the hook's markdown body.

Proposed Fix

Add permissionDecisionReason to the hookSpecificOutput:

return {
    "hookSpecificOutput": {
        "hookEventName": hook_event,
        "permissionDecision": "deny",
        "permissionDecisionReason": combined_message  # Add this line
    },
    "systemMessage": combined_message
}

Environment Info

  • Discovered after PermissionRequest hooks update (changelog: "Enable PermissionRequest hooks to process 'always allow' suggestions")
  • Hookify was merged in PR #11752 before permissionDecisionReason was available

Reproduction

  1. Create a hookify rule that blocks a command (e.g., hookify.require-uv.local.md)
  2. Have Claude try to run the blocked command
  3. Observe that Claude only sees "denied" without the detailed guidance

View original on GitHub ↗

8 Comments

basher83 · 9 months ago

Fix confirmed working locally.

Added permissionDecisionReason to hookSpecificOutput in rule_engine.py:

return {
    "hookSpecificOutput": {
        "hookEventName": hook_event,
        "permissionDecision": "deny",
        "permissionDecisionReason": combined_message  # Added this line
    },
    "systemMessage": combined_message
}

Claude now receives the full detailed blocking message (tables, explanations, guidance) instead of just "Hook PreToolUse:Bash denied this tool".

basher83 · 8 months ago

Related: UserPromptSubmit hooks have the same problem

Just discovered that UserPromptSubmit hooks also don't pass messages to Claude when using systemMessage.

Root Cause

For UserPromptSubmit hooks, the message must be in hookSpecificOutput.additionalContext, not systemMessage.

Fix

# Before (broken)
return {
    "systemMessage": message
}

# After (working)
return {
    "hookSpecificOutput": {
        "hookEventName": "UserPromptSubmit",
        "additionalContext": message
    }
}

Summary of hook output fields needed for Claude to receive messages:

| Hook Event | Field Required |
|------------|----------------|
| PreToolUse | hookSpecificOutput.permissionDecisionReason |
| PostToolUse | hookSpecificOutput.permissionDecisionReason |
| UserPromptSubmit | hookSpecificOutput.additionalContext |
| Stop | reason (for blocking) |

This should probably be documented more clearly in the hooks documentation - systemMessage appears to only be shown to the user, while hook-specific fields are needed to pass context to Claude.

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

anthony-spruyt · 7 months ago

So both warn and block reason does not get back to Claude. This defeats the purpose of these pretty much.

adrozdenko · 7 months ago

This is now fixed in hookify-plus!

Added permissionDecisionReason to the hookSpecificOutput so Claude sees the detailed blocking message:

"hookSpecificOutput": {
    "hookEventName": hook_event,
    "permissionDecision": "deny",
    "permissionDecisionReason": combined_message  # Now Claude sees WHY!
}
# Install hookify-plus
ln -sf /path/to/hookify-plus ~/.claude/plugins/cache/claude-code-plugins/hookify/0.1.0

hookify-plus aggregates community fixes that Anthropic hasn't merged. Feel free to use it! 🚀

coygeek · 7 months ago

Feature was implemented in v2.1.9.
-Added support for PreToolUse hooks to return additionalContext to the model

👍

anthony-spruyt · 7 months ago

Confirmed: Still broken in hookify@claude-plugins-official (January 2026)

Just tested with Claude Code 2.1.11 and the official hookify plugin. The fix described above was never merged.

Test Setup

  • Claude Code version: 2.1.11
  • Hookify rule with enabled: true (native plugin handling)
  • Rule: block base64 decode commands

Result

When the rule blocks a command, Claude receives only:

"Hook PreToolUse:Bash denied this tool"

Claude does NOT receive the detailed message explaining why it was blocked or what alternatives to use.

Proof - Current hookify source still missing the fix

Source: https://github.com/anthropics/claude-plugins-official/blob/main/plugins/hookify/core/rule_engine.py#L60-L79

# Line 62-63: combined_message contains the detailed rule message
messages = [f"**[{r.name}]**\n{r.message}" for r in blocking_rules]
combined_message = "\n\n".join(messages)

# Line 72-79: PreToolUse/PostToolUse response
return {
    "hookSpecificOutput": {
        "hookEventName": hook_event,
        "permissionDecision": "deny"
        # ❌ permissionDecisionReason is MISSING - Claude never sees combined_message!
    },
    "systemMessage": combined_message  # ✅ User sees this, but Claude doesn't
}

The combined_message has the detailed explanation, but it's only in systemMessage (shown to user). Claude needs it in permissionDecisionReason to understand why the command was blocked.

Workaround

We use a native hook bridge that outputs to stderr + exit 2, which Claude Code properly routes to the model. This works, but it's a workaround for what should be fixed in the plugin.

Request

Please add permissionDecisionReason to the hookSpecificOutput:

return {
    "hookSpecificOutput": {
        "hookEventName": hook_event,
        "permissionDecision": "deny",
        "permissionDecisionReason": combined_message  # ✅ Add this line
    },
    "systemMessage": combined_message
}

This is a one-line fix that would make hookify actually useful for guiding Claude's behavior.

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.