Skip to content

Fix Windows crash on exit: upgrade napi to 3.12.7 - #8

Merged
zippy merged 2 commits into
main-0.7from
fix/napi-windows-exit-crash
Sep 22, 2026
Merged

zippy merged 2 commits into
main-0.7from
fix/napi-windows-exit-crash

Conversation

@zippy

@zippy zippy commented Sep 22, 2026

Copy link
Copy Markdown
Member

Problem

On Windows, any process that has loaded this addon can crash as it exits, with 0xC0000005 (access violation). In an Electron 32 app, which is how Moss loads it, this happened on every quit. It showed up as an intermittent failure of the Windows Node 20 test job on the v0.700.0 tag run: all tests passed, then the process crashed on exit. The previous blank test never loaded the binding, so CI never saw it before.

Cause

napi 3.2.2 stops its tokio runtime from a process-exit destructor (#[ctor::dtor] fn thread_cleanup → shutdown_async_runtime()). On Windows, ExitProcess has already terminated the runtime's threads when that destructor runs, so the shutdown touches freed state. napi 3.6.0 moved native shutdown to a Node env cleanup hook, which runs while the threads are still alive (napi-rs/napi-rs#3026, "shutdown runtime at env cleanup on windows").

Measurement

On windows-latest, each case ran 40 times, each in a fresh process. The count is exits with 0xC0000005:

Case napi 3.2.2 napi 3.12.7
Electron 32 app: load, call, quit 40 / 40 0 / 40
Node worker thread: load, call 11 / 40 (Node 20), 31 / 40 (Node 22) 0 / 40
ava test suite 8 / 40, 9 / 40 0 / 40
Node main thread: load, call 0 / 40 0 / 40

Every we-rust-utils release so far is built on napi 3.2.2, including 0.601.x and 0.700.0.

Changes

  • napi 3.2.2 → 3.12.7 and napi-derive 3.2.2 → 3.6.8. The generated index.js / index.d.ts are unchanged, and the hash regression tests pass.
  • A new regression test: __test__/exit.spec.mjs runs a load-and-call probe 10 times in fresh processes, on the main thread and in a worker thread, and fails on any non-zero exit code. It runs in CI on every binding. At the worker crash rate measured on 3.2.2, 10 runs catch that kind of regression with near certainty.

After merge

Release 0.700.1 the same way as 0.700.0, then bump Moss main-0.7 to ^0.700.1.

Not in this PR

  • No version bump.
  • No backport to main-0.6. The 0.6 line has the same bug.

napi 3.2.2 shuts the tokio runtime down from a process-exit destructor
(#[ctor::dtor]). On Windows, ExitProcess has already terminated the
runtime's threads by then, so the shutdown touches freed state and the
process dies with 0xC0000005. napi 3.6.0 moved the shutdown to a Node
env cleanup hook, which runs while the threads are still alive
(napi-rs#3026).

Measured on windows-latest, 40 fresh processes per case, napi 3.2.2 vs
3.12.7:

  Electron 32 app, load + call + quit:  40/40 crash vs 0/40
  Node worker thread, load + call:      11-31/40   vs 0/40
  ava test suite:                        8-9/40    vs 0/40
  Node main thread, load + call:         0/40      vs 0/40

The Electron case is how Moss loads this addon, so Moss on Windows is
expected to crash on every quit with any release built on napi 3.2.2.
Runs a load-and-call probe 10 times in fresh processes, on the main
thread and in a worker thread, and asserts every exit code is 0. With
napi 3.2.2 the worker probe crashed on Windows in 11-31 of 40 runs, so
10 runs catch a regression of that kind with near certainty; with 3.12.7
it crashed in none. Runs in CI on every binding.
@zippy
zippy merged commit c6a290a into main-0.7 Sep 22, 2026
16 checks passed
@zippy
zippy deleted the fix/napi-windows-exit-crash branch September 22, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant