You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Kiln's Node range admits Windows releases where an RPC launch can crash at exit #218
Kiln's package.json declares "node": ">=22.19.0". That range admits Node releases on which a Windows --rpc launch can end with a crash status even though its work completed. The crash is a Node defect, not Kiln's, and is fixed in Node 24.20.0. Kiln neither documents nor checks the affected versions.
Follow-up to #206, which attributed the crash and was closed with no Kiln change.
On an affected version, Pi's RPC shutdown calls process.exit() and Node can abort with Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c and exit code 0xC0000409. Since #200 the supervisor reports that code correctly, so the launch ends with a crash status.
Not measured: Node 23.x, Node 24 releases before 24.18.0, and Node 25 and later. The Node issue names 23.x and later as affected. Which 25.x or later release first carries the fix has not been checked. Only Windows is affected.
Scope
Document or enforce the Node versions on which Windows RPC launches are safe. Out of scope: any delay, retry or suppression around Pi's exit, and any change to how the job host or supervisor reports the exit code.
Questions to decide
Document only, warn at launch, or refuse at launch on an affected Windows version.
Measured on Windows 11 Pro 10.0.26300 from main at bc7d8ca, with Pi 0.87.1 started directly: no Kiln extension, supervisor or job host, and the loopback provider fixture. Every Node build is the official win-x64/node.exe, checked against its release's SHASUMS256.txt.
Each run is two launches on one session id. The first creates the session and the second resumes it with a 256 KB answer, which makes Pi compact its context. That resumed launch is the workload #206 found to crash.
1. --mode rpc across every Node major the engines range admits
Ten runs per version. The client closes standard input after agent_settled.
Node
V8
Resumed launch crashed
22.19.0
12.4
0 of 10
22.23.3
12.4
0 of 10
23.0.0
12.9
10 of 10
23.11.1
12.9
10 of 10
24.0.0
13.6
10 of 10
24.18.1
13.6
10 of 10
24.19.0
13.6
9 of 10
24.20.0
13.6
0 of 10
24.21.0
13.6
0 of 10
25.0.0
14.1
10 of 10
25.9.0
14.1
10 of 10
26.0.0
14.6
10 of 10
26.5.0
14.6
9 of 10
26.6.0
14.6
10 of 10
26.7.0
14.6
0 of 10
26.11.1
14.6
0 of 10
Every crash was exit code 0xC0000409 with Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c. The first launch of each run, which creates the session, crashed 0 of 160 times.
2. The other launch modes
The same workload, on affected versions 23.0.0, 23.11.1, 24.0.0, 24.19.0, 25.9.0 and 26.6.0, and on 22.23.3, 24.20.0 and 26.7.0 as controls. Counts are resumed launches.
Pi mode
Kiln launch that uses it
Affected versions
Controls
Interactive, in a pseudo terminal, quit with /quit
terminal launch
0 of 18
0 of 9
Interactive, shut down by an extension on agent_end
recovery
0 of 36
0 of 18
Print (-p)
piped launch
0 of 48
0 of 24
JSON print (--mode json -p)
delegated children
0 of 48
0 of 24
Only RPC mode crashed. Reading Pi's code gives a reason that matches: RPC mode calls process.exit() straight after disposing the runtime, interactive mode first waits up to one second for terminal input to drain, and print modes return from main and let Node exit on its own.
3. Affected range, from Node's history and from the boundaries above
Introduced by the V8 12.8 update (nodejs/node#54077), which the bisect in nodejs/node#56645 points to. It first shipped in 23.0.0. Node 22 stays on V8 12.4.
Fixed by nodejs/node#61999, merged 2026-07-24. The changelogs list it in 24.20.0 and 26.7.0 only.
The 23.x and 25.x lines ended before the fix merged, so neither has a safe release.
Unsafe: all of 23.x, 24.0 through 24.19, all of 25.x, and 26.0 through 26.6. Safe: 22.x, 24.20.0 and later 24.x, 26.7.0 and later 26.x, and 27 onward.
4. Determinism
Not strictly deterministic. Two affected versions crashed 9 of 10, and the crash depends on the workload: a new session never crashed, and in #206 the same Pi with short answers crashed 0 of 160 launches on 24.18.0. In Kiln's own test/rpc-start.test.mjs on 24.18.0 the rate was 1 of 6 on main and 2 of 5 on a feature branch.
5. Where Kiln can check
Pi runs under process.execPath, so the launcher's Node is Pi's. main in bin/start-kiln.mjs knows the mode once arguments are parsed. A check fits immediately after the output mode is set, before the project root is resolved, before the launch checks, and before any process starts.
6. Decision
Refuse start-kiln --rpc on Windows under an unsafe Node, before anything starts, with exit status 2 and the stable code node-version-unsafe-for-rpc. There is no override. Terminal and piped launches and every non-Windows launch are unchanged. engines.node stays >=22.19.0: Node 22 is safe, the defect is Windows-only and one launch mode is affected, so there is no platform-independent unsafe range. The README and docs/running-the-shell.md will state the range.
Not measured
Linux and macOS. The Node issue reports Windows only.
Kiln's own Node processes, such as the supervisor and launcher, which also call process.exit().
Interactive mode was sampled at 3 runs per version for an operator quit and 6 for a programmatic shutdown.
Activity
don-milsey-miller commentedon Oct 13, 2026
Investigation outcome
Measured on Windows 11 Pro 10.0.26300 from
mainatbc7d8ca, with Pi 0.87.1 started directly: no Kiln extension, supervisor or job host, and the loopback provider fixture. Every Node build is the officialwin-x64/node.exe, checked against its release'sSHASUMS256.txt.Each run is two launches on one session id. The first creates the session and the second resumes it with a 256 KB answer, which makes Pi compact its context. That resumed launch is the workload #206 found to crash.
1.
--mode rpcacross every Node major theenginesrange admitsTen runs per version. The client closes standard input after
agent_settled.Every crash was exit code
0xC0000409withAssertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c. The first launch of each run, which creates the session, crashed 0 of 160 times.2. The other launch modes
The same workload, on affected versions 23.0.0, 23.11.1, 24.0.0, 24.19.0, 25.9.0 and 26.6.0, and on 22.23.3, 24.20.0 and 26.7.0 as controls. Counts are resumed launches.
/quitagent_end-p)--mode json -p)Only RPC mode crashed. Reading Pi's code gives a reason that matches: RPC mode calls
process.exit()straight after disposing the runtime, interactive mode first waits up to one second for terminal input to drain, and print modes return frommainand let Node exit on its own.3. Affected range, from Node's history and from the boundaries above
Unsafe: all of 23.x, 24.0 through 24.19, all of 25.x, and 26.0 through 26.6. Safe: 22.x, 24.20.0 and later 24.x, 26.7.0 and later 26.x, and 27 onward.
4. Determinism
Not strictly deterministic. Two affected versions crashed 9 of 10, and the crash depends on the workload: a new session never crashed, and in #206 the same Pi with short answers crashed 0 of 160 launches on 24.18.0. In Kiln's own
test/rpc-start.test.mjson 24.18.0 the rate was 1 of 6 onmainand 2 of 5 on a feature branch.5. Where Kiln can check
Pi runs under
process.execPath, so the launcher's Node is Pi's.maininbin/start-kiln.mjsknows the mode once arguments are parsed. A check fits immediately after the output mode is set, before the project root is resolved, before the launch checks, and before any process starts.6. Decision
Refuse
start-kiln --rpcon Windows under an unsafe Node, before anything starts, with exit status 2 and the stable codenode-version-unsafe-for-rpc. There is no override. Terminal and piped launches and every non-Windows launch are unchanged.engines.nodestays>=22.19.0: Node 22 is safe, the defect is Windows-only and one launch mode is affected, so there is no platform-independent unsafe range. The README anddocs/running-the-shell.mdwill state the range.Not measured
process.exit().Merge pull request #222 from don-milsey-miller/claude/issue-218-windo…