PreToolUse/PostToolUse hooks silently disabled in subagents due to CLAUDE_CODE_SIMPLE check in _R()
Bug Description
PreToolUse and PostToolUse hooks configured in ~/.claude/settings.json do not fire for tool calls made by subagents spawned via the Agent tool. The hooks work correctly for the main/parent agent session.
Root Cause (traced in cli.js v2.1.92)
The universal hook runner _R() has this guard at the top:
async function*_R({hookInput, toolUseID, matchQuery, signal, ...}) {
if (S18()) return; // disableAllHooks policy
if (U6(process.env.CLAUDE_CODE_SIMPLE)) return; // <-- THIS LINE
...
}
CLAUDE_CODE_SIMPLE is set to "1" when --bare mode is active. Subagents appear to run in a context where this env var is truthy, causing _R() to return immediately without executing any hooks.
The bb6() function that checks hook existence does NOT filter by agent context — it correctly finds settings hooks for subagents. But execution never reaches bb6() because _R() bails out first.
Reproduction
- Configure a PreToolUse:Bash command hook in
~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "/path/to/my-hook.sh"}]
}]
}
}
- In the main agent, run a Bash command → hook fires correctly
- Spawn a subagent via the Agent tool that runs Bash commands → hooks do NOT fire
Tested on v2.1.92, Windows 11, with both user-level and project-level settings.
Expected Behavior
PreToolUse and PostToolUse hooks should fire for tool calls made by subagents, same as for the main agent. The CLAUDE_CODE_SIMPLE guard in _R() should be scoped to only skip hooks when the current execution context is in simple/bare mode, not when any part of the process has set this env var.
Proposed Fix
Option A (minimal): In _R(), check a context-scoped flag instead of the global env var:
// Instead of:
if (U6(process.env.CLAUDE_CODE_SIMPLE)) return;
// Check the toolUseContext for simple mode:
if (O?.isSimpleMode || (!O && U6(process.env.CLAUDE_CODE_SIMPLE))) return;
Option B (broader): Don't use process.env for in-process state. Use a context object passed through the call chain.
Impact
- Security: Restrictions enforced via PreToolUse hooks (e.g., blocking dangerous commands, validating file writes) can be completely bypassed by spawning a subagent
- Functionality: Custom hook workflows (code quality checks, edit context injection, timeout guards) silently stop working in subagents with no error or warning
- Debugging difficulty: No log message indicates hooks were skipped. The
_R()function returns silently.
Related Issues
- #34692 — PreToolUse/PostToolUse hooks do not fire for subagent tool calls
- #21460 — [SECURITY] PreToolUse hooks not enforced on subagent tool calls
- #18392 — Hooks in agent frontmatter not executed for subagents
- #17688 — Skill-scoped hooks not triggered within plugins
- #27755 — SubagentStart/SubagentStop unreliable
Environment
- Claude Code v2.1.92
- Windows 11
- Hooks configured in
~/.claude/settings.json(user-level) - Verified by extracting and reading cli.js from npm package
@anthropic-ai/claude-code@2.1.92
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗