PreToolUse hooks don't fire for subagents (Agent tool)
Summary
PreToolUse hooks defined in ~/.claude/settings.json fire correctly for the main agent but are silently skipped for all subagents spawned via the Agent tool. This means any hook-based command rewriting, safety checks, or instrumentation only covers a fraction of actual tool calls in multi-agent workflows.
Impact
In a Conductor environment running parallel agents, 72% of sessions are subagents. With rtk installed as a PreToolUse hook to filter/compress Bash output (saving ~40% tokens on grep, find, git, gh commands), the main agent gets the optimization but every subagent runs raw commands — wasting tokens on unfiltered output.
Measured gap (30-day window):
- Main sessions: ~11K commands went through the hook → tokens saved
- Subagent sessions: ~7K commands bypassed the hook entirely → full token cost
- Estimated waste: ~3M tokens/month from subagent commands that would have been filtered
Reproduction
- Add a
PreToolUsehook to~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/my-hook.sh"
}
]
}
]
}
}
- In a session, run a Bash command directly — hook fires ✅
- Spawn a subagent via the
Agenttool that runs Bash commands — hook does NOT fire ❌
Evidence from session transcripts:
Main session transcript shows hook invocations interleaved with Bash commands:
"command":"/Users/.../.claude/hooks/rtk-rewrite.sh"
"command":"grep -rn ..."
"command":"/Users/.../.claude/hooks/rtk-rewrite.sh"
"command":"find ..."
Subagent transcript shows raw Bash commands with zero hook invocations:
"command":"grep -r ..."
"command":"find ..."
"command":"grep -n ..."
Expected behavior
All hooks (PreToolUse, PostToolUse, etc.) should propagate to subagents spawned by the Agent tool, inheriting from the same settings hierarchy (user → project → local) as the main agent.
Environment
- Claude Code version: latest (via Conductor)
- rtk version: 0.30.0
- OS: macOS (Darwin 25.5.0)
- Hook location:
~/.claude/settings.json(user-level)
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
The 72% subagent coverage gap you've documented here is a concrete argument for why hook propagation can't be optional. When subagents handle the majority of actual tool calls, any hook that only runs on the main session is effectively a partial safety net.
Two clarifying questions that might help Anthropic scope the fix:
subagent_type(e.g.,"code-writer"vs"general-purpose"), does the hook behavior change? If typed subagents have a different settings resolution path, that could narrow the root cause.The token waste number (3M tokens/month from unfiltered subagent commands) is useful framing. Safety-blocking hooks are the more critical argument though: a
PreToolUsehook that prevents destructive Bash commands from running is meaningless if 72% of Bash calls in a multi-agent session bypass it entirely.Worth noting: the companion issue #69283 (context inheritance for subagents) has a similar structure. Both issues share the pattern that multi-agent workflows silently drop properties the user configured at the top-level session -- hooks in this case, CLAUDE.md rules in #69283. They're separate fix surfaces but the same architectural gap: subagents don't inherit the parent session's configured environment.
This seems like an important gap to close. If subagents don't inherit the same hook chain as the parent, then any guarantees around safety, logging, or command rewriting become inconsistent. In practice, the most autonomous workflows are often the ones that spawn the most subagents.
I ran into a similar idea while looking at https://github.com/FailproofAI/failproofai. Features like loop detection, execution policies, or preventing premature completion only work if every agent in the hierarchy is subject to the same runtime controls. Otherwise the weakest link ends up being the subagent that bypasses them.
Coverage should be observable, not only fixed once.
Alongside propagating hooks, stamp each tool call or hook payload with a resolved settings hash or
hook_chain_id, and recordhook_evaluated: true|falseplus a skip reason when false.That lets users tell the difference between subagents inheriting no settings, hooks being skipped for a tool call, and settings loading correctly but no matcher firing. Without that receipt, this can regress silently back into partial coverage.
---
_Generated with ax._
This been raised again and again and never fixed. ! this is a no go for any real corporate use where audit trail and tokenomics are important. Please fix it.