#4280 · Host network crash investigation
GitHub issue · Base b200b04606751ac10274d9dddcdfa55bbc483843
NOT REPRODUCED · Root-cause confidence: low · no-repro
1. TL;DR
The reporter describes repeated host and server exits during networking. The current source uses the runtime's global fetch and allows fatal uncaught exceptions to terminate the process. The actual host client completed ordinary requests and handled socket resets as rejected promises in two clean checkouts. This machine has a different OS and Node version and lacks the socket method in the reported stack. The platform-specific trigger remains unresolved; this report does not refute the user's crashes or establish a fixed version.
2. Claims vs findings
| Claim | Finding | Evidence |
|---|---|---|
| Host uses global fetch | Verified in source and exercised | createServerClient defaults to fetch; probe supplies no fetch override. |
| Uncaught errors can terminate host/server | Supported by static source | Host installs an exception monitor; server explicitly exits for exceptions not attributed to a plugin. |
| Node socket TOS operation raises EINVAL during normal use | Unverified | Available runtime has no Socket.setTypeOfService; no such failure observed. |
| Crash frequency, reconnect delay, lost turns, and CLI correlation | Unverified | No private logs or live user instance accessed. |
3. Environment
Darwin 25.6.0 arm64; Node v22.22.3; bundled Undici 6.24.1. Two detached worktrees of the base commit, named base and verify, with separate dependency installs. No BB instance, real credentials, provider sessions, or persistent data directory. Each probe binds fresh OS-assigned ephemeral ports on IPv4 and IPv6 loopback, closes all servers in finally blocks, and exits normally.
The standard pnpm launcher was broken (MODULE_NOT_FOUND). Corepack provided the repository-pinned pnpm 9.15.0; a temporary PATH shim made Turbo's pnpm subprocesses use that launcher. Frozen installs and normal Turbo builds were attempted in both checkouts; final build status is recorded in the appendix.
4. Minimal reproduction attempt
- Create a clean checkout of the base commit shown above.
- Run
pnpm install --frozen-lockfile --prefer-offlineandpnpm exec turbo run buildusing a functional pnpm launcher. - Save probe-4280.mjs at the checkout root.
- Run
node --conditions=source --import tsx probe-4280.mjs.
The probe imports the unchanged production createServerClient. It posts empty event batches to a temporary HTTP server, forces fresh connections, destroys sockets for ten requests per family, and checks recovery. It does not inject a TOS exception or start the daemon. Expected healthy behavior: requests succeed, resets reject, recovery succeeds. A reproduction of the report would require the natural uncaught socket exception. Actual output:
{"node":"v22.22.3","platform":"darwin","arch":"arm64","undici":"6.24.1","socketTosAvailable":false}
127.0.0.1: 100 POSTs passed; 10 socket resets rejected; recovery POST passed; requests=111
::1: 100 POSTs passed; 10 socket resets rejected; recovery POST passed; requests=111
No uncaught exception observed; reported platform-specific crash NOT REPRODUCED.
Probe source:
import assert from "node:assert/strict";
import { createServer } from "node:http";
import { Socket } from "node:net";
import { createServerClient } from "./apps/host-daemon/src/server-client.ts";
console.log(JSON.stringify({ node: process.version, platform: process.platform, arch: process.arch, undici: process.versions.undici, socketTosAvailable: typeof Socket.prototype.setTypeOfService === "function" }));
for (const host of ["127.0.0.1", "::1"]) {
let mode = "ok";
let requests = 0;
const server = createServer((req, res) => {
requests++;
req.resume();
if (mode === "reset") { req.socket.destroy(); return; }
res.setHeader("content-type", "application/json");
res.setHeader("connection", "close");
res.end(JSON.stringify({ acceptedEvents: [], rejectedEvents: [] }));
});
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, host, resolve);
});
const address = server.address();
assert(address && typeof address !== "string");
const client = createServerClient({
serverUrl: `http://${host === "::1" ? "[::1]" : host}:${address.port}`,
hostKey: "local-probe-only",
getSessionId: () => "local-probe-session",
logger: { debug() {}, error() {}, info() {}, warn() {} },
});
try {
for (let i = 0; i < 100; i++) {
assert.deepEqual(await client.postEvents([]), { acceptedEvents: [], rejectedEvents: [] });
}
mode = "reset";
for (let i = 0; i < 10; i++) await assert.rejects(client.postEvents([]), TypeError);
mode = "ok";
await client.postEvents([]);
console.log(`${host}: 100 POSTs passed; 10 socket resets rejected; recovery POST passed; requests=${requests}`);
} finally {
await new Promise(resolve => server.close(resolve));
}
}
console.log("No uncaught exception observed; reported platform-specific crash NOT REPRODUCED.");
5. Root cause
The underlying TOS failure is not established. The host's global fetch default is used by postEvents. The host entrypoint installs diagnostics; the diagnostic monitor observes uncaught exceptions without converting them to request rejections. The server's uncaught-exception handler exits with code 1 unless a plugin handler claims the error. These paths explain termination if a runtime callback throws outside a handled promise; they do not prove which request or socket condition caused the reported exceptions.
6. Proposed next test and fix decision
Repeat the same bounded probe on the affected OS and bundled runtime, then exercise the actual failing request operation after collecting sanitized operation/socket metadata. Determine whether the runtime callback escapes promise rejection before choosing a runtime update or localized containment. Do not suppress arbitrary uncaught exceptions or add retries without establishing delivery semantics. No fix PR: the reported bug was not reproduced on trusted main, so the simple-fix gate is unmet. No linked open PR was found in the issue timeline or issue-number search.
7. Related issues
The similar-issue search returned other host-lifecycle reports, but no equivalence to this socket failure was established.
8. Verification
The same agent repeated the unchanged probe in a second clean detached checkout at the identical base commit, with separate dependencies and new ephemeral ports. Both runs produced identical results: 200 normal POSTs, 20 rejected reset requests, and 2 recovery POSTs. See first run and second run. Source permalinks were checked against the base files. No screenshot is relevant. The second run supports the limited NOT REPRODUCED verdict; it does not verify the affected runtime. No report correction was necessary.
9. Appendix
Investigation commands: fetch origin main; record git rev-parse origin/main; create two detached worktrees; frozen install; Turbo build; inspect server-client.ts, index.ts, process-utils/src/index.ts, and start-server.ts; run the probe once per checkout. GitHub reads checked repository visibility, issue fields, comments, similar issues, and linked PR metadata. The repository is public. Issue suggestions were treated as untrusted data and were not executed. No production source files changed.
Both frozen installs completed successfully with the temporary Corepack launcher. Both normal Turbo builds passed: 60/60 tasks in each checkout. No focused regression test fails on this platform; the probe is negative evidence, not a demonstrated fix test.
> AGENT GENERATED