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)

Status Open
Reported on v2.1.215
Maintainer reply None cached
Activity 2 comments · opened Jul 20, 2026

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)

  1. 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 in settings.json at the time — executed directly and returned a live 401, with no permission prompt shown. (session 6e2a9152-f7d6-4934-883f-5fd0e011687a, transcript lines 4/14/16/17)
  1. 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"}}
`
with exit code 0. The hook script itself is confirmed firing and deciding correctly (
~/.claude/hooks/gate-sensitive-hosts.log, entries at 2026-07-20T14:04:29Z` onward).

  1. The environment anomaly. env inside that live session showed:

``
CLAUDECODE=1
CLAUDE_CODE_SESSION_ID=6e2a9152-f7d6-4934-883f-5fd0e011687a
CLAUDE_CODE_CHILD_SESSION=1
CLAUDE_PID=22286
``

  1. 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
`
No parent
claude` process anywhere in the chain — this rules out the mundane "it's actually a subagent/child session" explanation for the env var.

  1. A persistent background daemon/supervisor exists and adopts worker processes across restarts. ~/.claude/daemon.log shows a long-running supervisor that self-restarts on version upgrades and, on each restart, logs bg adopt: adopted=N respawned=0 dead=0 (observed 2026-07-18T02:32:21Z and 2026-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 / the bg adopt entries at 2026-07-19T04:41:14Z are 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

  1. Is CLAUDE_CODE_CHILD_SESSION=1 expected to appear on a top-level claude launch under any documented circumstance (e.g. background-daemon adoption)? If so, please document it.
  2. Does an ask PreToolUse decision 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.
  3. 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.log shows daemon self-restarts across versions 2.1.212 → 2.1.214 → 2.1.215 with origin=transient

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗