Skip to content

Hook async property silently ignored — postToolUse hooks block tool completion #3063

@loganrosen

Description

@loganrosen

Describe the bug

The async property on command hooks in hooks.json is silently ignored. Hooks with "async": true still block tool completion because executeHooks() unconditionally awaits every hook in a sequential for loop.

Claude Code documents async as a supported hook field:

async — If true, runs in the background without blocking.

Since plugins share the same hooks.json format across both Claude Code and Copilot CLI, authors reasonably expect "async": true to work. Instead, it is parsed by the Zod schema (via .passthrough()) but never read by the execution path.

Impact

In a real session, a postToolUse hook with "async": true blocked for 13.5 seconds on first invocation because it triggered a package auto-update (resolving 419 packages via uv). This added ~14s of latency to the first tool call of the session despite the hook being marked async.

Timeline from session events:

18:57:09.756  hook.start   postToolUse
18:57:22.186  [hook stdout] Resolved 419 packages in 6.91s
18:57:22.803  [hook stdout] Installed 2 packages
18:57:23.216  hook.end     ← 13.46s blocked

Root cause

In sdk/index.js, the executeHooks function (Yg in minified source):

async function executeHooks(hooks, input, logger, hookType, emitter) {
  // ...
  for (let hook of hooks) {
    let result = await hook(input);  // ← always awaits, ignores async flag
    // ...
  }
}

And in processToolExecutionResult:

await executeHooks(this.getEffectiveHooks()?.postToolUse, ...)  // blocks tool completion

The async property from the hook config is never propagated to the execution layer.

Expected behavior

When a hook specifies "async": true, it should run in the background without blocking tool completion — matching Claude Code's documented behavior. Ideally also support asyncRewake for parity.

Workaround

Plugin authors can append & to the shell command to background it manually, but this loses stdout/stderr capture and exit code handling.

Environment

  • Copilot CLI: 1.0.40-2
  • OS: macOS (Darwin arm64)

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:pluginsPlugin system, marketplace, hooks, skills, extensions, and custom agentsarea:toolsBuilt-in tools: file editing, shell, search, LSP, git, and tool call behavior

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions