microsoft/typescript-go · critical
Child process exited with code ${this.child.exitCode} before
Error message
Child process exited with code ${this.child.exitCode} before pipe was ready What it means
On Windows, SyncRpcChannel spawns tsgo and retries openSync on the named pipe up to 500 times (10ms apart). If the child exits while the parent is still retrying, the failure is reported with the child's exit code. The pipe never appearing plus a non-zero (or early zero) exit means the server binary itself failed to start — the channel correctly surfaces the child's exit code instead of looping until timeout.
Source
Thrown at _packages/native-preview/src/api/syncChannel.ts:164
// readSync/writeSync can't be used on stdio pipes. Instead,
// we create a Windows named pipe path, pass it to the child
// via --pipe, and open it with fs.openSync which returns a
// real C-runtime fd backed by a proper HANDLE.
const pipePath = `\\\\.\\pipe\\tsgo-sync-${process.pid}-${Date.now()}`;
this.child = spawn(exe, [...args, "--pipe", pipePath], {
stdio: ["ignore", "ignore", "inherit"],
});
// Retry openSync until the child creates the named pipe.
let fd: number | undefined;
for (let i = 0; i < 500; i++) {
try {
fd = openSync(pipePath, "r+");
break;
}
catch {
if (this.child.exitCode !== null) {
throw new Error(
`Child process exited with code ${this.child.exitCode} before pipe was ready`,
);
}
Atomics.wait(sleepBuf, 0, 0, 10);
}
}
if (fd === undefined) {
this.child.kill();
throw new Error("SyncRpcChannel: timed out connecting to named pipe");
}
this.readFd = fd;
this.writeFd = fd;
this.pipeFd = fd;
}
else {
// POSIX: use stdio pipe file descriptors directly.
this.child = spawn(exe, args, {
stdio: ["pipe", "pipe", "inherit"],
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Run the configured tsserverPath binary manually with --api --cwd . to see its startup error
- Remove tsserverPath to use the bundled tsgo binary, which is verified to work with the client
- Check the exit code in the message (non-zero usually means crash; 0 often means wrong binary that exits cleanly)
Example fix
// before
new Client({ tsserverPath: "./bin/tsgo-old.exe" }); // exits at startup
// after
new Client({ cwd: projectRoot }); // use bundled binary Defensive patterns
Strategy: try-catch
Validate before calling
import { existsSync } from "node:fs";
const exeOk = (p?: string) => p === undefined || existsSync(p); Try / catch
try { api = new API({ tsserverPath }); } catch (e) { if ((e as Error).message.includes("before pipe was ready")) { /* log exit code, verify binary by running it manually, retry with bundled binary */ } else throw e; } Prevention
- Smoke-test a custom tsserverPath binary with '--api --cwd .' before wiring it in
- Prefer the bundled tsgo binary
- Surface the exit code from the message — it distinguishes crash (non-zero) from wrong binary (0)
When it happens
Trigger: tsserverPath pointing at a missing/corrupt executable; tsgo crashing at startup (incompatible build, missing OS libraries, bad --api/--cwd arguments); the binary exiting immediately for any reason before creating its pipe.
Common situations: Custom tsserverPath pointing at a wrong-architecture or wrong-version binary; Windows environments missing runtime DLLs; PATH issues where the executable resolves to a non-tsgo program that exits instantly.
Related errors
- SyncRpcChannel: timed out connecting to named pipe
- SyncRpcChannel: could not obtain pipe file descriptors.
- failed to create pipe transport: %w
- Invalid value for ${name}: ${value}
- Found external imports in .d.ts files:\n${importErrors.map(e
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/6174e5d6ee1d8678.
Report an issue: GitHub.