PreToolUse/PostToolUse hooks do not fire for subagent (Agent tool) tool calls

Status Closed — not planned
Reported on v2.1.76
Maintainer reply None cached
Activity 10 comments · opened Mar 15, 2026 · closed May 30, 2026

Bug Report

Claude Code version: 2.1.76
Platform: Linux (Fedora/Bazzite, kernel 6.17.7)
Shell: bash

Summary

When the main session spawns subagents via the Agent tool, any Bash, Edit, Write, Read, or Grep tool calls made by the subagent do not trigger PreToolUse or PostToolUse hooks configured in ~/.claude/settings.json. Only tool calls made directly by the main session thread fire the hooks.

This means any compliance, auditing, or safety hooks that depend on intercepting tool calls are silently bypassed when work is delegated to subagents — which is the recommended pattern for parallelized workflows.

Reproduction Steps

  1. Configure a PostToolUse hook in ~/.claude/settings.json:

``json
{
"hooks": {
"PostToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "/path/to/protocol-tracker.sh",
"timeout": 5
}]
}]
}
}
``

  1. The hook script increments a counter in a JSON file whenever a git commit command is detected in Bash tool calls.
  1. In a session, run git commit directly from the main thread — the hook fires and the counter increments. This works correctly.
  1. In the same session, spawn an Agent that runs git commit inside its Bash calls — the hook does not fire. The counter stays at zero.
  1. At end of session, the state file shows {"commits": 0} despite multiple commits having been made by subagents.

Expected Behavior

PreToolUse and PostToolUse hooks should fire for all tool calls within a session, regardless of whether the call originates from the main session or a subagent spawned by the Agent tool. The hook's stdin JSON should include an agent_id or source field to distinguish which agent made the call.

Actual Behavior

Hooks only fire for main-session tool calls. Subagent tool calls bypass all configured hooks entirely. There is no warning or indication that hooks are being skipped.

Impact

This is a compliance and safety gap for any workflow that uses hooks for:

  • Audit trail tracking (counting commits, ticket creation, deployments)
  • Security gates (blocking dangerous commands, checking for secrets in edits)
  • Protocol enforcement (ensuring business gates are checked before mutations)

In our case, we use hooks for gate enforcement (PreToolUse:Bash) and protocol compliance tracking (PostToolUse:Bash). Both are silently bypassed when we parallelize work via the Agent tool, which is the primary recommended workflow for performance.

Workaround

We query the actual systems of record (git log, Jira API) at checkpoint time rather than trusting hook-maintained counters, and reconcile the state file afterward. This makes the state file eventually consistent but loses the real-time warning capability.

Related Issues

  • #26923 — PreToolUse hook exit code 2 does not block Task tool calls
  • #5812 — Feature: Allow hooks to bridge context between subagents and parent
  • #16126 — Feature: Add agent identity to PreToolUse hook data
  • #14859 — Feature: Agent hierarchy in hook events
  • #22665 — Subagent does not inherit permission allowlist from settings.json

Suggested Fix

  1. Subagents should inherit and execute parent session hooks — same hooks, same matchers, same enforcement
  2. Hook stdin JSON should include agent contextagent_id, parent_agent_id, agent_name fields so hooks can distinguish source
  3. Documentation should note this limitation if inheritance is intentionally excluded

View original on GitHub ↗

7 Comments

github-actions[bot] · 5 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/21460

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

yurukusa · 5 months ago

Can confirm — subagent tool calls don't trigger parent-session hooks. I run subagents extensively (Explore, general-purpose, etc.) and none of their tool calls hit my PreToolUse/PostToolUse hooks.

This creates a fundamental trust gap: hooks are the enforcement layer, but the more you parallelize via subagents (which is the recommended pattern for complex tasks), the less enforcement you actually get. An audit hook that counts git commit calls will undercount. A safety hook that gates destructive Bash commands won't fire. The sophistication of the workflow is inversely proportional to the coverage of the safety net.

Hook inheritance from parent should be the default — subagents running in the same session context should respect the same policy. The agent_id/parent_agent_id fields in stdin JSON would be essential for hooks that need to distinguish source (e.g., different thresholds for main vs subagent).

Worth noting that #22665 (subagent doesn't inherit permission allowlist) describes the same pattern — subagent isolation is too aggressive in several dimensions, not just hooks.

The workaround of querying systems of record (git log, API state) at checkpoints instead of trusting hook counters is solid, but it shifts the burden from declarative policy to imperative polling, which defeats the purpose of the hook system.

prodan-s · 5 months ago

Still actively impacted by this issue. Our platform has 66 hooks — PreToolUse hooks not firing for sub-agent tool calls means our safety hooks (model-override-guard, protect-constitution, etc.) are bypassed in sub-agent context. PostToolUse confirmed working in sub-agents (tested v2.1.89), but PreToolUse blocking is critical for enforcement. Would appreciate this staying open.

fprochazka · 4 months ago

reproduced on v2.1.111

rwilk002 · 4 months ago

Empirical update — verified false in CLI 2.1.119 on Windows 11 / Git Bash.

Built a no-op JSON-logger PreToolUse hook (bash, reads stdin, appends to a JSONL file) and exercised it via claude -p --settings <scratch.json> --include-hook-events with a prompt that uses the Task tool to spawn a general-purpose subagent that runs Bash ls /tmp/.

Two PreToolUse fires were captured in the same session:

  1. The parent's Agent-tool invocation (the Task call itself) — payload contained tool_name: "Agent", tool_input.subagent_type: "general-purpose", no agent_id / agent_type (correctly — this is the parent context).
  1. The subagent's Bash call (~5 sec later) — payload contained tool_name: "Bash", tool_input.command: "ls /tmp/", AND both agent_id: "a9067acde1e8b8e72" and agent_type: "general-purpose".

PostToolUse for the subagent's Bash also fired with the same agent_id / agent_type populated. SubagentStart and SubagentStop fired similarly with the subagent's identity.

So the documented behavior in https://code.claude.com/docs/en/agent-sdk/hooks ("agent_id and agent_type are populated when the hook fires inside a subagent") matches what 2.1.119 actually does on Windows. If the original repro was on an older CLI version, this may be a "fixed in current" situation worth closing with that note. If it still reproduces on a current Linux build, there may be a platform-specific gap worth investigating separately.

Happy to share the test scratch config if useful — it's just a no-op logger script + a settings.json fragment wiring it to PreToolUse / PostToolUse / SubagentStart / SubagentStop.

github-actions[bot] · 3 months ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

B1tMaster · 1 month ago

Please fix it! Bug is still there. Its a huge issue for any real corporate use the audit trail and monitoring and tokenomics are a MUST.

Showing cached comments. Read the full discussion on GitHub ↗