github/copilot-sdk · error
CLI child process was unexpectedly started in parent…
Error message
CLI child process was unexpectedly started in parent process mode
What it means
connectToParentProcessViaStdio is used when the library runs as a child of an editor/host and should communicate over its own stdin/stdout. In that mode a CLI child process must NOT exist; finding this.cliProcess set means the client was misconfigured or the parent-mode connect was invoked from the wrong context. The library throws to prevent corrupting the host's stdin/stdout protocol.
Solutions
- Pick one hosting mode: either spawn the CLI as a child (use the child-process connect path) or run in parent process mode — never both on the same instance.
- In parent process mode, do not call start()/spawn before connecting; construct the client and immediately use the parent-mode connect.
- If you previously spawned a CLI child, discard that client instance and create a fresh one for parent-process mode.
- Verify the hosting-mode flag/configuration passed to CopilotClient matches the connect method being invoked.
Example fix
// before
const client = new CopilotClient({ ... });
await client.start(); // spawns cliProcess
await client.connectToParentProcessViaStdio(); // throws
// after
const client = new CopilotClient({ ... });
await client.connectToParentProcessViaStdio(); // no child spawned Defensive patterns
Strategy: validation
Validate before calling
if (hostingMode === 'parent-process') {
// must NOT have spawned a child
assert(!clientHasSpawnedChild, 'Do not call start() in parent-process mode');
} Try / catch
try {
await client.connectToParentProcessViaStdio();
} catch (err) {
if (err instanceof Error && err.message.includes('parent process mode')) {
client = new CopilotClient({ ...config }); // fresh instance, no spawn
await client.connectToParentProcessViaStdio();
}
throw err;
} Prevention
- Decide the hosting mode once from configuration and route to exactly one connect path.
- Never call start()/spawn before the parent-process connect.
- Use a brand-new client instance when switching modes.
- Keep mode selection centralized so child/parent paths cannot be mixed.
When it happens
Trigger: Calling the parent-process-mode connect path (e.g. connectToParentProcessViaStdio, typically via a flag indicating 'we are the plugin/child') while this.cliProcess has already been spawned by start() — i.e. mixing child-process hosting with parent-process wiring on the same client instance.
Common situations: Embedding the client inside an editor extension where the host expects to own stdio, but the code also spawned a CLI child; calling the wrong connect variant for the configured hosting mode; reusing a client configured for one mode with the other's connect call.
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
- CLI process not started
- Session not found
- FfiRuntimeHost is already closed.
- Runtime process not started
- CLI process not started
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/da2879536c62c056.
Report an issue: GitHub.
Appendix: source
Thrown at nodejs/src/client.ts:2941
});
// Create JSON-RPC connection over stdin/stdout
this.messageWriter = new TeardownResilientStreamMessageWriter(this.cliProcess.stdin!);
this.connection = createMessageConnection(
new StreamMessageReader(this.cliProcess.stdout!),
this.messageWriter
);
this.attachConnectionHandlers();
this.connection.listen();
}
/**
* Connect to parent via stdio pipes
*/
private async connectToParentProcessViaStdio(): Promise<void> {
if (this.cliProcess) {
throw new Error("CLI child process was unexpectedly started in parent process mode");
}
// Create JSON-RPC connection over stdin/stdout
this.messageWriter = new TeardownResilientStreamMessageWriter(process.stdout);
this.connection = createMessageConnection(
new StreamMessageReader(process.stdin),
this.messageWriter
);
this.attachConnectionHandlers();
this.connection.listen();
}
/**
* Connect to the CLI server via TCP socket
*/
private async connectViaTcp(): Promise<void> {
if (!this.runtimePort) {View on GitHub (pinned to cd8cf15dc3)