ask PreToolUse hook decision silently fails to surface in a top-level session carrying CLAUDE_CODE_CHILD_SESSION=1 (command executes instead of pausing for approval)
Summary
On a fresh, top-level terminal launch of claude (not spawned by any Claude-run subprocess), a PreToolUse hook configured to return permissionDecision: "ask" for a Bash command did not produce an interactive approval prompt. The command executed directly. The session's own environment carried CLAUDE_CODE_CHILD_SESSION=1, which per the current env-vars documentation should only appear on Claude-spawned subprocesses, not top-level CLI launches.
Confirmed facts (independently reproduced from on-disk evidence: hook log, daemon log, session transcript, live process tree)
- The fail-open itself. In a fresh top-level session,
curl -s -o /dev/null -w "%{http_code}\n" https://n8n.oibrio.com/api/v1/credentials— a host matched only by an"ask"entry insettings.jsonat the time — executed directly and returned a live401, with no permission prompt shown. (session6e2a9152-f7d6-4934-883f-5fd0e011687a, transcript lines 4/14/16/17)
- The hook logic is not the defect. The same hook script, replayed manually against matching stdin outside the live tool-call path, correctly returned:
``json`
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"Command references protected host: n8n.oibrio.com"}}
~/.claude/hooks/gate-sensitive-hosts.log
with exit code 0. The hook script itself is confirmed firing and deciding correctly (, entries at 2026-07-20T14:04:29Z` onward).
- The environment anomaly.
envinside that live session showed:
````
CLAUDECODE=1
CLAUDE_CODE_SESSION_ID=6e2a9152-f7d6-4934-883f-5fd0e011687a
CLAUDE_CODE_CHILD_SESSION=1
CLAUDE_PID=22286
- The process tree looks like an ordinary top-level launch, not a spawned subprocess.
```
22286 22240 claude
22240 22239 -zsh
22239 926 login -pf endamcquaid
claude` process anywhere in the chain — this rules out the mundane "it's actually a subagent/child session" explanation for the env var.
No parent
- A persistent background daemon/supervisor exists and adopts worker processes across restarts.
~/.claude/daemon.logshows a long-running supervisor that self-restarts on version upgrades and, on each restart, logsbg adopt: adopted=N respawned=0 dead=0(observed2026-07-18T02:32:21Zand2026-07-19T04:41:14Z). This confirms the daemon/prewarm-pool architecture is real and routinely active on this machine, which is the mechanism under suspicion for the env-var anomaly.
Hypothesis (NOT independently confirmed — flagging per your request rather than asserting)
That top-level sessions launched while this background daemon/prewarm pool is active get adopted into it, which sets CLAUDE_CODE_CHILD_SESSION=1 even though the session is not actually a Claude-spawned subprocess, and that in this adopted state an ask permissionDecision has no interactive terminal to resolve against and silently falls through to execution instead of blocking.
This causal chain is plausible and consistent with facts 1–5 above, but I have not reproduced it under a controlled A/B (same host pattern, same hook, daemon-adopted session vs. verified non-adopted session, both attempted live). A documentation research pass (via a sub-agent, not primary source I read myself) found CLAUDE_CODE_CHILD_SESSION documented only for Claude-spawned-subprocess contexts — supporting "this shouldn't be set here" — but found no documentation either confirming or denying that daemon adoption is the mechanism, or that it affects ask prompt delivery specifically.
Also unverified — do not include as fact
- The user's characterization of this as a "fourth confirmation" — I have direct transcript evidence of exactly one live-fire occurrence in the session reviewed.
- Any claim that
pid=2723/ thebg adoptentries at2026-07-19T04:41:14Zare part of "tonight's" incident — those log lines are timestamped the day before the reproduction above (2026-07-20). The mechanism (bg adopt on every daemon restart) is confirmed recurring; that specific pid is not evidence from the same night.
Workaround in place
The affected host patterns (n8n.oibrio.com, api.hetzner.cloud, api.supabase.com, plus an SSH target) were converted from "ask" to "deny" in settings.json, since deny is unconditional and doesn't depend on an interactive prompt reaching a human. This was verified working in a later session (this one): the same curl was blocked with Command references protected host: n8n.oibrio.com.
Ask
- Is
CLAUDE_CODE_CHILD_SESSION=1expected to appear on a top-levelclaudelaunch under any documented circumstance (e.g. background-daemon adoption)? If so, please document it. - Does an
askPreToolUsedecision have a defined fallback behavior when no interactive terminal/prompt channel is available (fail open vs. fail closed)? Current observed behavior is fail-open, which seems like the wrong default for a security-relevant gate. - Is there a supported way to detect or opt a session out of daemon/prewarm-pool adoption, for cases where a hard interactive boundary is required?
Environment
- Claude Code version: 2.1.215
- Platform: macOS (Darwin)
~/.claude/daemon.logshows daemon self-restarts across versions 2.1.212 → 2.1.214 → 2.1.215 withorigin=transient
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗