From 6d7b2e6ce72e302a497e35384027b026685c8eb0 Mon Sep 17 00:00:00 2001 From: Dmitrij Rozdestvensky Date: Wed, 5 Aug 2026 16:31:46 +0200 Subject: [PATCH] fix(cli): don't hard-exit `download:plugins` after fetching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `download:plugins` handler called `process.exit()` on both its success and failure paths, as soon as `downloadPlugins` settled. On Windows that aborts the process outright: Node calls `uv_async_send` on an already-closing handle while undici tears down the sockets the OVSX requests ran on, libuv asserts, and the process fast-fails. Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 76 The process dies with exit code 3221226505 (0xC0000409) after every plugin has been resolved and downloaded, so the work has succeeded and only the teardown fails. It is a race, so it reproduces intermittently — in one downstream CI the Windows packaging leg aborted on 4 of 6 runs, each time at the end of a `download:plugins` that had just reported every plugin as already downloaded. This is nodejs/node#56645, fixed upstream by nodejs/node#61999 but not present in any released Node version yet. It affects Node 23 and later; the same downstream job ran 36 Windows legs on Node 22 without a single abort. Set `process.exitCode` and let the event loop drain instead, matching what the `.fail()` handler in this file already does. The command still exits promptly: on Node 24.15.0 a cold run downloading two plugins takes 4.5s and exits 0, and a fully cached run exits in under a second, so the hard exit was not holding the process open. Signed-off-by: Dmitrij Rozdestvensky --- dev-packages/cli/src/theia.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dev-packages/cli/src/theia.ts b/dev-packages/cli/src/theia.ts index 9d315de4a255b..be327f119ebd9 100644 --- a/dev-packages/cli/src/theia.ts +++ b/dev-packages/cli/src/theia.ts @@ -432,13 +432,16 @@ async function theiaCli(): Promise { if (!client) { client = new OVSXHttpClient(apiUrl, requestService, rateLimiter); } + // This handler must not call `process.exit`: on Windows that aborts the + // process, because Node calls `uv_async_send` on an already-closing handle + // while undici tears down the connections used to fetch the plugins + // (nodejs/node#56645). Let the event loop drain instead. try { await downloadPlugins(client, rateLimiter, requestService, options); } catch (error) { console.error(error); - process.exit(1); + process.exitCode = 1; } - process.exit(0); }, }) .command<{