Skip to content

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

Description

@danielbodorin

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 raceuv_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: nodejs/node#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 libuv assertion on Windows with Node.js 23.x 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:platform-windowsWindows-specific: PowerShell, cmd, Git Bash, WSL, Windows Terminal

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions