{"record":{"id":"49f072298c63f12f","repo":"github/copilot-sdk","slug":"cli-process-not-started","errorCode":null,"errorMessage":"CLI process not started","messagePattern":"CLI process not started","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/client.ts","lineNumber":2902,"sourceCode":"            return true;\n        }\n        if (entrypoint.includes(`copilot-linux-${process.arch}`)) {\n            return false;\n        }\n        const report = process.report?.getReport();\n        const header =\n            report && \"header\" in report\n                ? (report.header as { glibcVersionRuntime?: string })\n                : undefined;\n        return header !== undefined && header.glibcVersionRuntime === undefined;\n    }\n\n    /**\n     * Connect to child via stdio pipes\n     */\n    private async connectToChildProcessViaStdio(): Promise<void> {\n        if (!this.cliProcess) {\n            throw new Error(\"CLI process not started\");\n        }\n\n        // Keep stdin pipe errors inside the normal JSON-RPC teardown path.\n        // Preserve the failure reason via the gated debug log rather than discarding it.\n        this.cliProcess.stdin?.on(\"error\", (err) => {\n            if (this.forceStopping) {\n                return;\n            }\n            this.state = \"error\";\n            const reason = err instanceof Error ? (err.stack ?? err.message) : String(err);\n            const stderrOutput = this.stderrBuffer.trim();\n            this.processTransportError = new Error(\n                `CLI server connection failed: ${reason}${stderrOutput ? `\\nstderr: ${stderrOutput}` : \"\"}`\n            );\n            this.logDebug(`stdin pipe error: ${reason}`);\n            try {\n                this.connection?.dispose();\n            } catch {","sourceCodeStart":2884,"sourceCodeEnd":2920,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/client.ts#L2884-L2920","documentation":"connectToChildProcessViaStdio wires JSON-RPC over the spawned CLI child's stdio pipes, but the child process handle (this.cliProcess) is null. The library throws because it cannot attach stdin/stdout listeners without a started process. It indicates the connect() sequence was invoked before spawn, or the child failed to start.","triggerScenarios":"Calling connect() (which routes to connectToChildProcessViaStdio) on a CopilotClient configured for child-process mode when the CLI process was never spawned — e.g. start/process creation failed silently, spawn was skipped, or connect() is called twice after teardown cleared cliProcess.","commonSituations":"Double-calling connect() without awaiting the first call, calling connect() after stop()/teardown reset internal state, or a spawn failure (bad CLI path) that left cliProcess unset while the connect step still ran.","solutions":["Ensure the CLI process is started before connecting: await the start/spawn step and verify the CLI binary path resolves.","Call connect() exactly once per client lifecycle; create a new CopilotClient instead of reconnecting after stop().","Await each async lifecycle call — avoid firing connect() concurrently or before start() resolves.","Check spawn configuration (env, cwd, CLI path) so process creation does not fail before the stdio hookup."],"exampleFix":"// before\nconst client = new CopilotClient({ ... });\nclient.connect(); // not awaited, races with stop() elsewhere\n\n// after\nconst client = new CopilotClient({ ... });\nawait client.start(); // spawn first\nawait client.connect(); // then connect, once","handlingStrategy":"try-catch","validationCode":"if (client.isStopped?.() || !client) {\n  throw new Error('Cannot connect: client lifecycle already terminated');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await client.start();\n  await client.connect();\n} catch (err) {\n  if (err instanceof Error && err.message === 'CLI process not started') {\n    // recreate client and redo start()+connect() in order\n  }\n  throw err;\n}","preventionTips":["Always await start() (spawn) before connect().","Call connect() exactly once per client instance.","Never reuse a client after stop(); create a new instance.","Validate the CLI binary path exists before spawning."],"tags":["lifecycle","process-spawn","stdio","state"],"backgroundTag":"invalid-state-transition","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}