github/copilot-sdk · error

CLI process not started

Error message

CLI process not started

What it means

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.

Solutions

  1. Ensure the CLI process is started before connecting: await the start/spawn step and verify the CLI binary path resolves.
  2. Call connect() exactly once per client lifecycle; create a new CopilotClient instead of reconnecting after stop().
  3. Await each async lifecycle call — avoid firing connect() concurrently or before start() resolves.
  4. Check spawn configuration (env, cwd, CLI path) so process creation does not fail before the stdio hookup.

Example fix

// before
const client = new CopilotClient({ ... });
client.connect(); // not awaited, races with stop() elsewhere

// after
const client = new CopilotClient({ ... });
await client.start(); // spawn first
await client.connect(); // then connect, once
Defensive patterns

Strategy: try-catch

Validate before calling

if (client.isStopped?.() || !client) {
  throw new Error('Cannot connect: client lifecycle already terminated');
}

Try / catch

try {
  await client.start();
  await client.connect();
} catch (err) {
  if (err instanceof Error && err.message === 'CLI process not started') {
    // recreate client and redo start()+connect() in order
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/49f072298c63f12f. Report an issue: GitHub.

Appendix: source

Thrown at nodejs/src/client.ts:2902

            return true;
        }
        if (entrypoint.includes(`copilot-linux-${process.arch}`)) {
            return false;
        }
        const report = process.report?.getReport();
        const header =
            report && "header" in report
                ? (report.header as { glibcVersionRuntime?: string })
                : undefined;
        return header !== undefined && header.glibcVersionRuntime === undefined;
    }

    /**
     * Connect to child via stdio pipes
     */
    private async connectToChildProcessViaStdio(): Promise<void> {
        if (!this.cliProcess) {
            throw new Error("CLI process not started");
        }

        // Keep stdin pipe errors inside the normal JSON-RPC teardown path.
        // Preserve the failure reason via the gated debug log rather than discarding it.
        this.cliProcess.stdin?.on("error", (err) => {
            if (this.forceStopping) {
                return;
            }
            this.state = "error";
            const reason = err instanceof Error ? (err.stack ?? err.message) : String(err);
            const stderrOutput = this.stderrBuffer.trim();
            this.processTransportError = new Error(
                `CLI server connection failed: ${reason}${stderrOutput ? `\nstderr: ${stderrOutput}` : ""}`
            );
            this.logDebug(`stdin pipe error: ${reason}`);
            try {
                this.connection?.dispose();
            } catch {

View on GitHub (pinned to cd8cf15dc3)