github/copilot-sdk · error · RuntimeError
CLI process not started
Error message
CLI process not started
What it means
RuntimeError raised when creating the stdio JSON-RPC client if self._process is None, i.e. no CLI subprocess exists. The stdio transport wires JSON-RPC over the process's stdin/stdout, which cannot work without a started process.
Solutions
- Start the CLI process (client.start() or the appropriate spawn call) before creating the stdio client
- Do not reuse a client instance after stop(); create a new CopilotClient
- Ensure the start() await completed successfully before calling stdio connect
- Check custom code isn't setting self._process = None on cleanup prematurely
Example fix
// before await client._create_stdio_client() # no process yet -> RuntimeError // after await client.start() # spawns CLI process await client._create_stdio_client()
Defensive patterns
Strategy: try-catch
Validate before calling
assert client._process is not None, "start() must complete before stdio connect"
Type guard
def has_process(client) -> bool:
return getattr(client, "_process", None) is not None Try / catch
try:
await client._create_stdio_client()
except RuntimeError as e:
if "CLI process not started" in str(e):
await client.start()
await client._create_stdio_client()
else:
raise Prevention
- Await start() before any connect call
- Create a new client after stop() instead of reconnecting the old one
- Keep transport mode (stdio/tcp/ffi) consistent across start and connect
- Encapsulate start+connect in one helper so ordering can't drift
When it happens
Trigger: Calling _create_stdio_client() (stdio connect path) before the CLI process was spawned, or after it was stopped/cleared (self._process reset to None).
Common situations: Calling connect with stdio mode before start(); reusing a client after stop()/disconnect; a failed prior start left _process None; incorrect ordering of internal API calls in custom tooling.
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
- CLI child process was unexpectedly started in parent…
- Runtime process not started
- Server port not available
- Session not found
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/f0f48470be8dfb7c.
Report an issue: GitHub.
Appendix: source
Thrown at python/copilot/client.py:4594
await self._connect_via_tcp()
log_timing(
logger,
logging.DEBUG,
"CopilotClient._connect_to_server transport setup complete",
setup_start,
)
async def _connect_via_stdio(self) -> None:
"""
Connect to the CLI server via stdio pipes.
Creates a JSON-RPC client using the CLI process's stdin/stdout.
Raises:
RuntimeError: If the CLI process is not started.
"""
if not self._process:
raise RuntimeError("CLI process not started")
# Create JSON-RPC client with the process
self._client = JsonRpcClient(self._process)
self._client.on_close = self._handle_connection_close
self._rpc = ServerRpc(self._client)
# Set up notification handler for session events
# Note: This handler is called from the event loop (thread-safe scheduling)
def handle_notification(method: str, params: dict):
if method == "session.event":
session_id = params["sessionId"]
event_dict = params["event"]
# Convert dict to SessionEvent object
event = session_event_from_dict(event_dict)
with self._sessions_lock:
session = self._sessions.get(session_id)
if session:
session._dispatch_event(event)View on GitHub (pinned to cd8cf15dc3)