github / github/copilot-cli

Copilot CLI crashes on exit (Windows) — libuv `uv_async_send` on a closing handle (FAST_FAIL_FATAL_APP_EXIT)

Open
#4,217 0 comments 1 reaction 0 assignees View on GitHub
area:platform-windows
Dominant language
Shell
Stars
11.2k
Forks
1.9k
Avg merge
14h 16m
Merged PRs (30d)
6

Description

### Describe the bug

On Windows x64, `copilot.exe` **consistently crashes at process exit** with a fatal
fail-fast (`0xc0000409`, subcode `0x7 FAST_FAIL_FATAL_APP_EXIT`). The session's work
completes normally; the crash happens only during teardown.

Live-debugger analysis (WinDbg attached to the crashing process) shows this is the
well-known **Windows-only Node.js/libuv shutdown race** — `uv_async_send()` is called
on a `uv_async_t` whose `UV_HANDLE_CLOSING` flag is already set, tripping the libuv
assertion:

```
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c
```

which calls `abort()` → `__fastfail(FAST_FAIL_FATAL_APP_EXIT)`. The bundled Node
(v24.16.0) ships libuv with this assertion **enabled**, so the otherwise-benign
teardown race becomes a hard crash.

Upstream tracking issue: https://github.com/nodejs/node/issues/56645
(Node 23/24/25 lines on Windows; Linux/macOS unaffected.)

### Affected version

- **GitHub Copilot CLI:** 1.0.74-0 (crash first captured on 1.0.73.0; reproduces on 1.0.74-0) - **Bundled Node.js:** v24.16.0 (win-x64, single-executable build), ICU 78 - **OS:** Windows x64 (build 10.0.29635) - **Onset:** started ~1 month ago (consistent with a CLI update that bumped the bundled Node)

### Steps to reproduce the behavior

1. Launch `copilot` on Windows x64.
2. Use it normally (any session involving network/async activity).
3. Exit the CLI.
4. The process aborts on exit with a fail-fast crash / WER entry (exit is not clean).

Reproduces every time on the affected machine.

### Expected behavior

The CLI should exit cleanly (exit code 0, no fail-fast/WER crash) after the session ends.

### Additional context

Two threads show both sides of the shutdown race:

**Crashing thread — "V8Worker" (a V8 background platform worker):**
```
copilot!uv_async_send+0x3a
→ _wassert("!(handle->flags & UV_HANDLE_CLOSING)", "src\win\async.c", 94)
→ abort()
→ __fastfail(FAST_FAIL_FATAL_APP_EXIT) ; int 29h, rcx=7
```
Disassembly of `uv_async_send` confirms the fatal branch is the top-of-function
guard (`test byte ptr [handle+0x58],1` / `UV_HANDLE_CLOSING`), not a failed
`PostQueuedCompletionStatus`.

**Main thread — in Node's process-exit teardown, joining workers:**
```
node::DefaultProcessExitHandler
→ (V8 platform shutdown)
→ uv_thread_join
→ WaitForSingleObjectEx ; waiting for the V8 worker to exit
```

So during `DefaultProcessExitHandler`, the main thread closes the per-isolate
"flush-tasks" `uv_async_t` (sets `UV_HANDLE_CLOSING`) and joins the V8 workers; a
V8 worker drains one last task and calls `uv_async_send()` on that now-closing
handle → assertion → abort.

`!analyze -v` summary:
- `ExceptionCode: c0000409` (STATUS_STACK_BUFFER_OVERRUN), `Subcode: 0x7 FAST_FAIL_FATAL_APP_EXIT`
- `FAILURE_BUCKET_ID: FAIL_FAST_FATAL_APP_EXIT_c0000409_copilot.exe!Unknown`
- Faulting thread is the V8 worker; instruction is `int 29h` (`__fastfail`).

## Suggested fixes (for maintainers)
- Avoid `process.exit()` during teardown when V8/libuv async handles are still
live; prefer setting `process.exitCode` and letting the loop drain, or add a
small deferral before forced exit (common community workaround for #56645).
- Ship a Node build with the libuv fix for nodejs/node#56645 once available, or a
Node line where the shutdown ordering doesn't hit this path on Windows.
- Impact is at-exit only (no data loss), but it produces a non-zero exit and a WER
crash entry on every exit, which is noisy and can break scripts that check the
CLI's exit code.

Contributor guide

Open the contributing guide

Research direction

Start by tracing the Node DefaultProcessExitHandler and V8 platform shutdown described in the report, focusing on the process.exit path and the per-isolate flush-tasks uv_async_t. Reproduce the exit crash on Windows x64 and compare the bundled Node v24.16.0 behavior with the upstream nodejs/node#56645 tracking issue. Done means copilot exits cleanly with code 0 and no fail-fast or WER crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.