Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 16, 2026

Plan to fix JSON-RPC large payload issue

  • Explore repository and understand the issue
  • Implement _read_exact() helper method in jsonrpc.py
  • Update _read_message() to use _read_exact() for reading content
  • Create focused unit test for large payload handling
  • Run tests to validate the fix
  • Run formatter to ensure code style consistency
  • Review and finalize changes
Original prompt

This section details on the original issue you should resolve

<issue_title>JSON-RPC read fails with large payloads (>64KB) due to pipe short reads</issue_title>
<issue_description>## Summary

The JSON-RPC client in copilot/jsonrpc.py fails to read large responses (>64KB) because _read_message() assumes read(n) always returns exactly n bytes. On Unix pipes, this is not guaranteed—the kernel may return fewer bytes ("short read"), especially when data exceeds the pipe buffer size.

Reproduction

Send a prompt with ~35KB+ of context. The response (~72KB with context echo) exceeds the 64KB pipe buffer:

import asyncio
from copilot import CopilotClient

async def test():
    client = CopilotClient()
    await client.start()
    
    session = await client.create_session({
        "model": "claude-sonnet-4.5",
        "streaming": False,
        "available_tools": [],
    })
    
    # 35KB context triggers the bug
    context = "Status update: work in progress. " * 1000  # ~35KB
    await session.send({"prompt": f"Summarize: {context}"})
    # ... wait for response
    
asyncio.run(test())

Error:

JSON-RPC read loop error: Unterminated string starting at: line 1 column 36026 (char 36025)

Root Cause

In copilot/jsonrpc.py, the _read_message() method:

content_length = int(header.split(":")[1].strip())
# ...
content_bytes = self.process.stdout.read(content_length)  # BUG: assumes full read
content = content_bytes.decode("utf-8")
return json.loads(content)

When content_length exceeds the pipe buffer (64KB on macOS, varies on Linux), read() returns only what is currently buffered. The truncated bytes fail JSON parsing.

Evidence

Diagnostic testing shows the exact behavior:

Context Size Expected Response Received Missing
35KB 72,094 bytes 65,513 bytes 9.1%
40KB 82,258 bytes 65,514 bytes 20.4%
45KB 92,554 bytes 65,512 bytes 29.2%

Note: Received bytes are consistently ~65,512 (≈64KB), the macOS pipe buffer limit.

Suggested Fix

Read in a loop until all expected bytes are received:

def _read_exact(self, num_bytes: int) -> bytes:
    """Read exactly num_bytes, handling partial/short reads from pipes."""
    chunks = []
    remaining = num_bytes
    while remaining > 0:
        chunk = self.process.stdout.read(remaining)
        if not chunk:
            raise EOFError("Unexpected end of stream while reading JSON-RPC message")
        chunks.append(chunk)
        remaining -= len(chunk)
    return b"".join(chunks)

def _read_message(self) -> Optional[dict]:
    # ... existing header parsing ...
    content_bytes = self._read_exact(content_length)  # Use loop-based read
    # ... rest unchanged ...

Environment

  • OS: macOS 14.x (also affects Linux with different buffer sizes)
  • Python: 3.11
  • SDK version: Latest from pip

Workaround

Limit context size to ~30KB to keep responses under 64KB.

References

  • POSIX read() specification: reads from pipes may return fewer bytes than requested
  • macOS pipe buffer: 64KB (PIPE_SIZE in XNU kernel)
  • Linux pipe buffer: typically 64KB (configurable via fcntl(F_SETPIPE_SZ))</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits January 16, 2026 15:59
- Add _read_exact() method to handle short reads from pipes
- Update _read_message() to use _read_exact() for reliable large payload handling
- Add comprehensive unit tests for short read scenarios and large payloads

Co-authored-by: SteveSandersonMS <[email protected]>
Copilot AI changed the title [WIP] Fix JSON-RPC read issue for large payloads Fix JSON-RPC pipe reads >64KB by handling short reads Jan 16, 2026
@SteveSandersonMS SteveSandersonMS marked this pull request as ready for review January 19, 2026 13:18
@SteveSandersonMS SteveSandersonMS requested a review from a team as a code owner January 19, 2026 13:18
Copilot AI review requested due to automatic review settings January 19, 2026 13:18
@SteveSandersonMS
Copy link
Contributor

@copilot Re-run python formatter

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a critical bug in the JSON-RPC client where reading large responses (>64KB) failed due to Python's read() not guaranteeing to return all requested bytes from pipes. The fix implements a loop-based reading approach that accumulates chunks until all expected bytes are received.

Changes:

  • Added _read_exact() method to handle partial/short reads from pipes
  • Updated _read_message() to use _read_exact() instead of direct read()
  • Added comprehensive unit tests covering various payload sizes and edge cases

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
python/copilot/jsonrpc.py Added _read_exact() method that loops until all requested bytes are read, updated _read_message() to use it
python/test_jsonrpc.py New test file with mock classes (MockProcess, ShortReadStream) and comprehensive test coverage for the fix including 64KB boundary cases, large payloads, EOF handling, and sequential messages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor Author

Copilot AI commented Jan 19, 2026

@copilot Re-run python formatter

Done. Formatted with ruff format in commit 5ce1d19.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON-RPC read fails with large payloads (>64KB) due to pipe short reads

2 participants