Skip to content

fix(server): Sequence destroy() callback after native teardown - #1513

Open
RandomByte wants to merge 1 commit into
mainfrom
fix/server-destroy-teardown-race
Open

fix(server): Sequence destroy() callback after native teardown#1513
RandomByte wants to merge 1 commit into
mainfrom
fix/server-destroy-teardown-race

Conversation

@RandomByte

@RandomByte RandomByte commented Aug 7, 2026

Copy link
Copy Markdown
Member

Fixes a unit test failing flakily in CI, most often on Node 22/24 on Windows, with test/lib/server/reinitialize.js exited with a non-zero exit code: 3221225477. That code is 0xC0000005, a native access violation on process teardown, not a test-assertion failure. The defect is in production teardown code, so the fix lands in Supervisor.js; the flaky reinitialize test is what surfaced it.

Root cause

Supervisor.destroy() passed the caller's callback to http.Server.close(), firing it on socket close. The BuildServer's teardown (the @parcel/watcher unsubscribe and the node:sqlite handle, which maps a 256 MB region of the database into memory) runs after that call and was not sequenced ahead of the callback, so server.close(resolve) resolved with SQLite still mid-close. The next test.serial opening a fresh DatabaseSync, or the worker exiting, then races the finalizing handle and raises an access violation on Windows (0xC0000005), surfacing as exited with a non-zero exit code: 3221225477 in the reinitialize suite.

Fix

Start the socket close, then await the definition watcher and BuildServer teardown, and await the socket last. The callback fires only once every native handle is closed.

Supervisor.destroy() passed the caller's callback to http.Server.close(),
firing it on socket close. The BuildServer's teardown (the @parcel/watcher
unsubscribe and the node:sqlite handle, which maps a 256 MB region of the
database into memory) runs after that call and was not sequenced ahead of
the callback, so server.close(resolve) resolved with SQLite still mid-close.
The next test.serial opening a fresh DatabaseSync, or the worker exiting,
then races the finalizing handle and raises an access violation on Windows
(0xC0000005), surfacing as `exited with a non-zero exit code: 3221225477`
in the reinitialize suite.

Start the socket close, then await the definition watcher and BuildServer
teardown, and await the socket last. The callback fires only once every
native handle is closed.
@RandomByte
RandomByte requested a review from a team August 7, 2026 09:57
@RandomByte
RandomByte marked this pull request as ready for review August 7, 2026 09:57
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