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
- 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)
- 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
- 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).
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗