Filed by the implementing agent for #15545, session session_01D47qPfEWVPmhguWgBZCi5N, from a measurement made while repairing that card. Filed bare on purpose: domain:*, type and priority are triage's.
The class, as measured on #15545
#15545 was one TypeError: fetch failed / SocketError: other side closed / bytesRead: 0 in a merge-group run, and it could not be attributed at all — because the probe that produced it discarded the two artefacts that decide what happened: the spawned child's exit status, and everything the child printed. The child.on('exit') handler in that file fed the READINESS promise only, so once readiness had settled a later death was invisible and the rejection reached vitest as a bare TypeError: fetch failed with no exit code, no stdout and no stderr.
That was reproduced deterministically on this tree, on that file, with the pre-repair code: injecting a peer that accepts the whole request and then FINs produced
TypeError: fetch failed
Caused by: SocketError: other side closed
Serialized Error: { code: 'UND_ERR_SOCKET', socket: { ... bytesWritten: 349, bytesRead: +0 } }
byte-identical in shape to the merge-group report, with nothing in the message to say whether the child had died or was still running.
The other three files with the same shape
packages/cli/test/ holds four files that fetch a spawned os serve over HTTP. #15545's PR repairs one of them, deliberately in that file only (its scope was one card). The other three still have the unguarded shape — a fetch inside a try whose finally stops the child, with no read of the child's fate on the failing path:
packages/cli/test/serve-process-child-env.e2e.test.ts — 1 probe (the sign-in/email origin-posture probe)
packages/cli/test/serve-mcp-stdio-answers.e2e.test.ts — 2 probes (auth/sign-in/email, keys)
packages/cli/test/serve-mcp-capability-collision.e2e.test.ts — 3 probes (auth/sign-in/email, keys, mcp/skill)
serve-port-drift-notice.e2e.test.ts also calls fetch, but against the neighbour fixture it holds itself rather than against a spawned os serve — ⛔ it is NOT in this population and should not be swept in.
Why it is worth a card rather than a shrug
Each of these runs on Test Core, a required shard, so any occurrence is a merge-queue eviction that costs every lane a queue cycle — and under the one-re-run rule a second unlucky roll is recorded as a real red. The cost of the missing attribution is that the FIRST occurrence teaches nothing: #15545 has exactly one observation and it still cannot say which lifecycle event caused it.
Also relevant to the whole class: there is no process.on('uncaughtException') and no process.on('unhandledRejection') handler in this repo's product source, so a late async throw inside a spawned child takes the process down with no HTTP answer and exactly this client-side signature.
⛔ Whatever the repair is, it is not skipping, quarantining or bumping a timeout — same fence as #15545.
Re-check
grep -rn "await fetch(" packages/cli/test/serve-process-child-env.e2e.test.ts packages/cli/test/serve-mcp-stdio-answers.e2e.test.ts packages/cli/test/serve-mcp-capability-collision.e2e.test.ts
Then read each call's enclosing try and confirm the catch/finally never consults child.exitCode or child.signalCode, and never attaches the captured child output to the thrown error.
Dedup
search_issues on this session, twice, with a live positive control (both queries returned #15545 itself, so the search was not in the silently-zeroed mode). Nearest neighbours are all closed and about a different property: #12548 (which port that probe asserts against), #12526, #12525, #12441. Open #15273 is a different finding in a different file (a port-pick race in serve-publishes-bound-port).
Refs: #15545 and its PR.
Filed by the implementing agent for #15545, session
session_01D47qPfEWVPmhguWgBZCi5N, from a measurement made while repairing that card. Filed bare on purpose:domain:*, type and priority are triage's.The class, as measured on #15545
#15545 was one
TypeError: fetch failed/SocketError: other side closed/bytesRead: 0in a merge-group run, and it could not be attributed at all — because the probe that produced it discarded the two artefacts that decide what happened: the spawned child's exit status, and everything the child printed. Thechild.on('exit')handler in that file fed the READINESS promise only, so once readiness had settled a later death was invisible and the rejection reached vitest as a bareTypeError: fetch failedwith no exit code, no stdout and no stderr.That was reproduced deterministically on this tree, on that file, with the pre-repair code: injecting a peer that accepts the whole request and then FINs produced
byte-identical in shape to the merge-group report, with nothing in the message to say whether the child had died or was still running.
The other three files with the same shape
packages/cli/test/holds four files thatfetcha spawnedos serveover HTTP. #15545's PR repairs one of them, deliberately in that file only (its scope was one card). The other three still have the unguarded shape — afetchinside atrywhosefinallystops the child, with no read of the child's fate on the failing path:packages/cli/test/serve-process-child-env.e2e.test.ts— 1 probe (thesign-in/emailorigin-posture probe)packages/cli/test/serve-mcp-stdio-answers.e2e.test.ts— 2 probes (auth/sign-in/email,keys)packages/cli/test/serve-mcp-capability-collision.e2e.test.ts— 3 probes (auth/sign-in/email,keys,mcp/skill)serve-port-drift-notice.e2e.test.tsalso callsfetch, but against the neighbour fixture it holds itself rather than against a spawnedos serve— ⛔ it is NOT in this population and should not be swept in.Why it is worth a card rather than a shrug
Each of these runs on
Test Core, a required shard, so any occurrence is a merge-queue eviction that costs every lane a queue cycle — and under the one-re-run rule a second unlucky roll is recorded as a real red. The cost of the missing attribution is that the FIRST occurrence teaches nothing: #15545 has exactly one observation and it still cannot say which lifecycle event caused it.Also relevant to the whole class: there is no
process.on('uncaughtException')and noprocess.on('unhandledRejection')handler in this repo's product source, so a late async throw inside a spawned child takes the process down with no HTTP answer and exactly this client-side signature.⛔ Whatever the repair is, it is not skipping, quarantining or bumping a timeout — same fence as #15545.
Re-check
Then read each call's enclosing
tryand confirm thecatch/finallynever consultschild.exitCodeorchild.signalCode, and never attaches the captured child output to the thrown error.Dedup
search_issueson this session, twice, with a live positive control (both queries returned #15545 itself, so the search was not in the silently-zeroed mode). Nearest neighbours are all closed and about a different property: #12548 (which port that probe asserts against), #12526, #12525, #12441. Open #15273 is a different finding in a different file (a port-pick race inserve-publishes-bound-port).Refs: #15545 and its PR.