Repository navigation
Description
What happens
The native-terminal acceptance on the Windows runner fails intermittently in its teardown. The test itself passes; the file's afterAll hook times out waiting for the native host to say it closed.
This is not the failure that #17 fixed. #17 moved the host's exit into the process.send callback, so the host goes once it has reported — that covered the five-second drag that came after the report (#22). Here the host never reaches the report at all: it stalls inside the service close, one step earlier.
How it shows up
1) packages/webui/test/browser/native-runtime.spec.mjs:94:1 › V04/V06: native PTYs survive another page, reload and a full five-slot roster
"afterAll" hook timeout of 30000ms exceeded.
> 43 | test.afterAll(async () => {
Error: the native host did not finish closing
The Windows job reports 484 passed / 1 failed.
Evidence
- Run
42953031674, Windows job attempt 1 (job130301566263): failed as above. - The same commit, re-run as attempt 2 (job
130313055925): 485 passed / 0 failed. - The test body took 6.8 s in the failing run and 6.4 s in the passing one — the same order of magnitude as the runs that always passed (6.4 s to 7.9 s), so the body is not what failed.
- Earlier Windows runs (
42906391703,42856963545,42849887547) all passed this file.
Where it stops
Since #17 the host reports each step of its own shutdown on stderr, so a shutdown that does not finish says which step stopped answering. The failing run's last line is:
[native-host] service.close
It never reaches terminals exited. The runtime's own shutdown lines (Turn controller shutdown, All MCP connections closed) follow, and then nothing.
The host's shutdown order is service.close() → terminals exited → apiHost.close() → terminals checked → data dir removed → reported. So the stall is inside service.close().
WebuiService.close() runs the owners in order and awaits each: terminal, websocket-connections, websocket, browser-bridge, miniapp-proxies, http-listener, then the harness port. The runtime logs its own shutdown inside that sequence, so the stall is at or after the harness port close.
What is known and what is not
Known: the body passes, the failure is the teardown, it is not reproducible on demand, the host stalls inside the service close rather than after it, and it is not the mechanism #22 describes.
Not known: which owner inside the close sequence stalls on the runner, and whether it is the port close itself or something the port close waits for.
Why it matters
A flake in a gate is a gate nobody trusts. This one lands on the Windows runner, so it will arrive on any PR that happens to run there, dressed as a failure of the thing being changed.
Where it should be fixed
The service close has no deadline: every owner is awaited indefinitely, and one owner that never answers holds the whole shutdown. Two things would make the next occurrence both faster to diagnose and survivable:
- bound each owner's close, so a stalled owner is named instead of hanging the shutdown;
- have the host report the step it reached even when the service close does not finish, so the trace survives the stall.
Both live in packages/webui, so they can be fixed in this repository. The node-pty drag in #22 cannot be fixed from here — that timer is internal to node-pty.
Notes
- This file does not exist upstream:
MiniMax-AI/minimax-codehas nopackages/webuidirectory at all, so there is nowhere upstream to report this. - The change under review when this was seen was PR feat(webui): ship the wordmark's own font with the client #29, which does not touch this path.
native-runtime.spec.mjsimports onlytestfrom the harness and never callsopenApp, and the harness change in that PR is entirely insideopenApp.
Activity
modacker commentedon Nov 11, 2026
More evidence, and the upstream attribution
Three things are worth adding to this issue, all checked against the logs.
1. The node-pty upstream already carries this
The symptom is node-pty's, and it is already recorded there:
resolve([this._innerPid])).No new upstream issue is needed for any of these.
2. This repository's own preload reaches node-pty's helper
The acceptance host forks itself with the tsx loader in
execArgv:node-pty forks its console-list helper with
child_process.fork, which inheritsprocess.execArgv— so the helper runs under tsx. The failing run's host log shows exactly that:Two such crashes are in the host's captured log. That is the shape of #974, and it is specific to this repository: the preload is ours.
3. Two corrections to the numbers above
ok 161 … (6.5s)).42946296900, is a different failure: its four failures are the win32 visual baselines (issue [Bug]: the four win32 visual baselines no longer match — the MiniMax logo sits lower than the recorded image #27), andnative-runtimepassed there in 7.2 s. So the run above is the only observation of this failure in the window.Where this must not go
packages/webui/docs/agents/issue-tracker.mdis explicit: "Nothing in this repository may be published to upstream: upstream synchronization runs by three-way merge, so anything pushed or filed there shows up as unreviewed content in the public projection." The upstream for a webui defect is notMiniMax-AI/minimax-code— that repository has nopackages/webuiat all. It ismicrosoft/node-pty, and the issues are already open there.The fix, when it lands here
Two places, both inside
packages/webui, and both in the shape PR #17 established — measure, fix the cause, and leave the trace saying which step stalled:packages/webui/src/server/service.ts:352-377—#closeOwners()awaits every owner with no deadline. One owner that never answers holds the whole shutdown.packages/webui/test/browser/support/native-browser-host.ts:65-97— the host should report the step it reached even whenservice.close()does not finish, so the next occurrence says more than a timeout does.Both are in this repository and neither needs anything from upstream to land.