{"record":{"id":"426900dd8aeb3e1f","repo":"github/copilot-sdk","slug":"unexpected-end-of-stream-while-reading-json-rpc-me","errorCode":null,"errorMessage":"Unexpected end of stream while reading JSON-RPC message","messagePattern":"Unexpected end of stream while reading JSON-RPC message","errorType":"exception","errorClass":"EOFError","httpStatus":null,"severity":"error","filePath":"python/copilot/_jsonrpc.py","lineNumber":343,"sourceCode":"    def _read_exact(self, num_bytes: int) -> bytes:\n        \"\"\"\n        Read exactly num_bytes, handling partial/short reads from pipes.\n\n        Args:\n            num_bytes: Number of bytes to read\n\n        Returns:\n            Bytes read from stream\n\n        Raises:\n            EOFError: If stream ends before reading all bytes\n        \"\"\"\n        chunks = []\n        remaining = num_bytes\n        while remaining > 0:\n            chunk = self.process.stdout.read(remaining)\n            if not chunk:\n                raise EOFError(\"Unexpected end of stream while reading JSON-RPC message\")\n            chunks.append(chunk)\n            remaining -= len(chunk)\n        return b\"\".join(chunks)\n\n    def _read_message(self) -> dict | None:\n        \"\"\"\n        Read a single JSON-RPC message with a Content-Length header (blocking).\n\n        Returns:\n            Parsed JSON message, or None if the connection is closed.\n        \"\"\"\n        # Read header line\n        header_line = self.process.stdout.readline()\n        if not header_line:\n            return None\n\n        # Parse Content-Length\n        header = header_line.decode(\"utf-8\").strip()","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/python/copilot/_jsonrpc.py#L325-L361","documentation":"_read_exact reads exactly num_bytes from the child process's stdout; if the stream returns an empty chunk before the count is satisfied, the stream ended unexpectedly and this EOFError is raised. The JSON-RPC message being read is therefore truncated and unusable.","triggerScenarios":"The Copilot CLI child process exited or closed stdout mid-message — crash, kill, stdout corruption, or partial line write — while _read_message was reading the frame body.","commonSituations":"CLI binary crashing due to missing dependencies or bad args; process killed by OOM or signal; piping stdout somewhere that truncates output.","solutions":["Check the child process's stderr and exit code to find why the CLI died, and fix that root cause (bad args, missing deps).","Re-run and verify a stable CLI version; reinstall the CLI/runtime if the binary is corrupt.","Capture stdout/stderr in a pipe (not a terminal or file redirection that truncates).","Wrap the reader in retry logic that restarts the client process after unexpected EOF."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if process.poll() is not None:\n    raise RuntimeError(\"CLI process already exited; check stderr before RPC\")","typeGuard":null,"tryCatchPattern":"try:\n    msg = client.read_message()\nexcept EOFError as e:\n    if \"Unexpected end of stream\" in str(e):\n        exit_code = process.wait(); log(process.stderr.read()); restart_client()","preventionTips":["Capture and monitor the CLI process's stderr and exit status.","Keep the CLI binary healthy (correct version, dependencies installed).","Restart the client automatically on unexpected EOF.","Avoid piping stdout through anything that can truncate."],"tags":["jsonrpc","eof","subprocess"],"backgroundTag":"broken-pipe","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}