github / github/copilot-cli

Hook `async` property silently ignored — postToolUse hooks block tool completion

Offen
#3,063 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area:plugins area:tools
Vorherrschende Sprache
Shell
Sterne
11.2k
Forks
1.9k
Ø Merge
14 Std. 16 Min.
Gemergte PRs (30 T.)
6

Beschreibung

## 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 `await`s every hook in a sequential `for` loop.

Claude Code [documents `async`](https://docs.anthropic.com/en/docs/claude-code/hooks) 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):

```javascript
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`:
```javascript
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

- #2893 — hooks serialized + timeout fail-open (related hook execution model concerns)

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Start by tracing executeHooks in sdk/index.js and its call from processToolExecutionResult, then inspect how hook configuration reaches the execution layer. Confirm how async hooks should complete in the background and what output, errors, and tool completion should look like; done means async hooks no longer block while synchronous hooks retain their current behavior.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
javascript
Bereich
cli
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Ruhig
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
48/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.