PreToolUse hook deny not enforced for Agent tool calls

Resolved 💬 4 comments Opened Apr 7, 2026 by terry-li-hm Closed May 19, 2026

Summary

PreToolUse hooks returning permissionDecision: "deny" are not enforced for Agent tool calls. The deny JSON is correctly output by the hook, but Claude proceeds to spawn the agent anyway.

Reproduction

  1. Configure a PreToolUse hook that denies Agent calls with a specific subagent_type:
# In PreToolUse hook
def guard_agent(data):
    ti = data.get("tool_input", {})
    subtype = ti.get("subagent_type", "")
    if subtype == "Explore":
        print(json.dumps({
            "hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "deny",
                "permissionDecisionReason": "Explore agents blocked by policy"
            }
        }))
        sys.exit(0)
  1. Running the hook standalone confirms correct output:
echo '{"tool": "Agent", "tool_input": {"subagent_type": "Explore", "prompt": "test", "run_in_background": true}}' \
  | python3 hook.py
# Output: {"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", ...}}
# Exit: 0
  1. Despite the hook returning deny, Claude spawns the Explore agent and it runs to completion.

Expected behavior

The Agent tool call should be blocked, same as deny works for Bash, Read, Write, Edit, etc.

Observed behavior

  • Without run_in_background: hook deny seems ignored, user gets a permission prompt (can manually reject)
  • With run_in_background: true: agent launches and completes with no interception

Environment

  • Claude Code v2.1.92
  • Linux (Fly.io)

Additional context

The permissions.deny field in settings.json also doesn't support Agent subagent type filtering (e.g., "Agent(Explore)" has no effect).

View original on GitHub ↗

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