Skip to content

Subagent tool calls: PreToolUse denials don't surface to the parent, and hook payload carries the parent's session_id #76726

Description

@wshallwshall

What happened

I use PreToolUse deny hooks to enforce a worktree-per-session policy. Building that enforcement, I hit two ways it silently fails for subagent (Task/Agent) tool calls. Both fail silent, not safe — the guard is off and nothing tells you.

Bug 1 — a subagent's permission denials don't surface to the parent.
With a PreToolUse deny in place, subagent tool calls were correctly blocked by the hook — but the parent session's result came back with permission_denials: [] (empty). A fan-out of subagents appears to succeed while having written nothing. There is no signal to the parent (or the user) that the guard fired inside the subagents.

Bug 2 — a subagent's hook payload carries the PARENT's session_id.
The JSON payload delivered to a hook invoked for a subagent's tool call reports the parent session's session_id, not a distinct one for the subagent. Any session-keyed lock or claim registry is therefore silently broken for subagents: N parallel subagents all present the same session_id and all pass the same mutex.

These compound: you can't build a reliable session-scoped guard (Bug 2), and even when a guard does fire in a subagent, the parent can't tell (Bug 1).

Steps to reproduce

  1. Configure a PreToolUse hook that returns a deny decision for some tool (e.g. Edit/Write to a given path).
  2. From a parent session, spawn one or more subagents (Task/Agent) that attempt that denied tool call.
  3. Observe the subagents are blocked, then inspect the parent's result payload.

Expected

  • Bug 1: the parent's result reflects that subagent tool calls were denied — permission_denials is populated (or there is some equivalent signal), so a fan-out that wrote nothing does not read as success.
  • Bug 2: the hook payload for a subagent's tool call carries an identifier that distinguishes the subagent from its parent, so session-keyed coordination works.

Actual

  • Bug 1: parent result shows permission_denials: []; the fan-out looks successful despite writing nothing.
  • Bug 2: the subagent hook payload's session_id equals the parent's.

Impact

Any hook-based enforcement (worktree isolation, path guards, claim/lock registries) that needs to cover subagent activity has a hole in it, and the hole is invisible at runtime. This is the single most time-consuming class of problem I hit building cross-session guards.

Related

Environment

  • Solo dev, Windows 11, VS Code extension + Desktop app.
  • Observed while running many concurrent sessions against one repo (~20 git worktrees, 15–20 sessions at a time).
  • Caveat: n=1 — one machine, one repo. Reproduced consistently in my setup; offered as evidence, not a general claim.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions