{"record":{"id":"843d5ac549886a75","repo":"github/copilot-sdk","slug":"process-not-started-or-stdout-not-available","errorCode":null,"errorMessage":"Process not started or stdout not available","messagePattern":"Process not started or stdout not available","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/copilot/client.py","lineNumber":4472,"sourceCode":"            self._cli_process = self._process\n        log_timing(\n            logger,\n            logging.DEBUG,\n            \"CopilotClient._start_cli_server subprocess spawned\",\n            spawn_start,\n        )\n\n        # For stdio mode, we're ready immediately\n        if use_stdio:\n            return\n\n        # For TCP mode, wait for port announcement\n        loop = asyncio.get_event_loop()\n        process = self._process  # Capture for closure\n\n        async def read_port():\n            if not process or not process.stdout:\n                raise RuntimeError(\"Process not started or stdout not available\")\n            while True:\n                line = await loop.run_in_executor(None, process.stdout.readline)\n                if not line:\n                    raise RuntimeError(\"CLI process exited before announcing port\")\n\n                line_str = line.decode() if isinstance(line, bytes) else line\n                logger.debug(\"[CLI] %s\", line_str.rstrip())\n                match = re.search(r\"listening on port (\\d+)\", line_str, re.IGNORECASE)\n                if match:\n                    self._runtime_port = int(match.group(1))\n                    return\n\n        try:\n            port_wait_start = time.perf_counter()\n            await asyncio.wait_for(read_port(), timeout=10.0)\n            log_timing(\n                logger,\n                logging.DEBUG,","sourceCodeStart":4454,"sourceCodeEnd":4490,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/python/copilot/client.py#L4454-L4490","documentation":"RuntimeError raised in CopilotClient's TCP-mode startup read_port() helper when the CLI subprocess handle is missing or its stdout stream is unavailable. The library needs to read the port announcement line from CLI stdout before it can connect, so without a live stdout it cannot proceed with TCP startup.","triggerScenarios":"Calling start()/connect in TCP mode when self._process was never set (CLI not spawned) or was created without a stdout pipe, so read_port() sees process=None or process.stdout=None.","commonSituations":"Calling _start_cli_server before spawning the process; the process handle was cleared after a crash/stop; a custom spawn path built the Popen/asyncio process without stdout=PIPE; attempting restart after a previous failed start left stale state.","solutions":["Ensure the CLI subprocess is started (via the library's normal start path) before connecting in TCP mode","Verify the spawn options include stdout=PIPE (or equivalent) so the port announcement can be read","If the process died, create a fresh CopilotClient/restart rather than reusing the stale instance","Check that no custom subclass or monkeypatch clears self._process before the port wait runs"],"exampleFix":"// before\nawait client._start_cli_server()  # process never spawned -> RuntimeError\n\n// after\nawait client.start()  # library path spawns the CLI, then waits for port announcement","handlingStrategy":"try-catch","validationCode":"if client._process is None or client._process.stdout is None:\n    raise RuntimeError(\"CLI must be started before TCP connect\")","typeGuard":"def cli_ready(client) -> bool:\n    return getattr(client, \"_process\", None) is not None and client._process.stdout is not None","tryCatchPattern":"try:\n    await client.start()\nexcept RuntimeError as e:\n    if \"Process not started or stdout not available\" in str(e):\n        await client.start()  # fresh spawn via the official start path\n    else:\n        raise","preventionTips":["Always start the client through the public start() API","Never reuse a client instance after stop()","Confirm subprocess spawn includes stdout piping","Avoid reaching into private _process state in application code"],"tags":["subprocess","startup","tcp"],"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"}