{"record":{"id":"6174e5d6ee1d8678","repo":"microsoft/typescript-go","slug":"child-process-exited-with-code-this-child-exitco","errorCode":null,"errorMessage":"Child process exited with code ${this.child.exitCode} before pipe was ready","messagePattern":"Child process exited with code (.+?) before pipe was ready","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"_packages/native-preview/src/api/syncChannel.ts","lineNumber":164,"sourceCode":"            // readSync/writeSync can't be used on stdio pipes. Instead,\r\n            // we create a Windows named pipe path, pass it to the child\r\n            // via --pipe, and open it with fs.openSync which returns a\r\n            // real C-runtime fd backed by a proper HANDLE.\r\n            const pipePath = `\\\\\\\\.\\\\pipe\\\\tsgo-sync-${process.pid}-${Date.now()}`;\r\n            this.child = spawn(exe, [...args, \"--pipe\", pipePath], {\r\n                stdio: [\"ignore\", \"ignore\", \"inherit\"],\r\n            });\r\n\r\n            // Retry openSync until the child creates the named pipe.\r\n            let fd: number | undefined;\r\n            for (let i = 0; i < 500; i++) {\r\n                try {\r\n                    fd = openSync(pipePath, \"r+\");\r\n                    break;\r\n                }\r\n                catch {\r\n                    if (this.child.exitCode !== null) {\r\n                        throw new Error(\r\n                            `Child process exited with code ${this.child.exitCode} before pipe was ready`,\r\n                        );\r\n                    }\r\n                    Atomics.wait(sleepBuf, 0, 0, 10);\r\n                }\r\n            }\r\n            if (fd === undefined) {\r\n                this.child.kill();\r\n                throw new Error(\"SyncRpcChannel: timed out connecting to named pipe\");\r\n            }\r\n            this.readFd = fd;\r\n            this.writeFd = fd;\r\n            this.pipeFd = fd;\r\n        }\r\n        else {\r\n            // POSIX: use stdio pipe file descriptors directly.\r\n            this.child = spawn(exe, args, {\r\n                stdio: [\"pipe\", \"pipe\", \"inherit\"],\r","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/_packages/native-preview/src/api/syncChannel.ts#L146-L182","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nnew Client({ tsserverPath: \"./bin/tsgo-old.exe\" }); // exits at startup\n\n// after\nnew Client({ cwd: projectRoot }); // use bundled binary","handlingStrategy":"try-catch","validationCode":"import { existsSync } from \"node:fs\";\nconst exeOk = (p?: string) => p === undefined || existsSync(p);","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["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)"],"tags":["typescript","spawn","windows","named-pipe","startup"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}