Bug Description
On Windows platforms, fetch will cause an exception (Assertion failed..) to be thrown on process.exit() call, if it's called shortly after a fetch() execution. This could affect fetch calls within CLI logic. Why it happens:
- Native
fetch is undici. On a successful request undici returns the
socket to a keep-alive pool (default keepAliveTimeout = 4s) instead of
closing it. The fetch controller does not abort/close on success.
- The pooled socket keeps libuv handles alive (a TCP handle + a
uv_async_t/timer).
process.exit(0) tears the event loop down immediately. libuv closes handles;
if undici's async handle is mid-teardown, libuv double-closes it and asserts.
Windows' src/win/async.c is strict about this; POSIX usually isn't.
Reproduction
See: https://github.com/art-alexeyenko/node-fetch-handles
Steps to reproduce (if not using the script above):
- Use the reproduction repo on a Windows platform
- Run a mock server (
npm run server)
- Run the repro (
npm run repro). This will:
a. Perform a fetch request to the local endpoint
b. Execute process.exit(0)
Expected Behavior
No exception at the end.
Actual Behavior
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 94 error occurs upon the process.exit call.
Logs & Screenshots
Sample log output
npm run repro
> undici-exit-crash-repro@1.0.0 repro
> node repro.mjs
Sent 2 requests
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 94
Environment
- OS: Windows
- Node.js version: >=24
- undici version: 7.29.0
Bug Description
On Windows platforms, fetch will cause an exception (
Assertion failed..) to be thrown on process.exit() call, if it's called shortly after a fetch() execution. This could affect fetch calls within CLI logic. Why it happens:fetchis undici. On a successful request undici returns thesocket to a keep-alive pool (default
keepAliveTimeout= 4s) instead ofclosing it. The fetch controller does not abort/close on success.
uv_async_t/timer).process.exit(0)tears the event loop down immediately. libuv closes handles;if undici's async handle is mid-teardown, libuv double-closes it and asserts.
Windows'
src/win/async.cis strict about this; POSIX usually isn't.Reproduction
See: https://github.com/art-alexeyenko/node-fetch-handles
Steps to reproduce (if not using the script above):
npm run server)npm run repro). This will:a. Perform a fetch request to the local endpoint
b. Execute
process.exit(0)Expected Behavior
No exception at the end.
Actual Behavior
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 94error occurs upon the process.exit call.Logs & Screenshots
Sample log output
Environment