chenhg5/cc-connect · error
read body: %w
Error message
read body: %w
What it means
After validating the Content-Length header, readMessage allocates a buffer of exactly contentLength bytes and uses io.ReadFull to read the body. If the stream ends (io.EOF/io.ErrUnexpectedEOF) or any read error occurs before contentLength bytes arrive, the error is wrapped as "read body: %w".
Source
Thrown at agent/copilot/jsonrpc.go:120
n, err := strconv.Atoi(strings.TrimSpace(val))
if err != nil {
return nil, fmt.Errorf("invalid Content-Length: %w", err)
}
contentLength = n
}
// Ignore other headers (Content-Type, etc.)
}
if contentLength < 0 {
return nil, fmt.Errorf("missing Content-Length header")
}
if contentLength > 10*1024*1024 {
return nil, fmt.Errorf("Content-Length too large: %d", contentLength)
}
body := make([]byte, contentLength)
if _, err := io.ReadFull(lr.reader, body); err != nil {
return nil, fmt.Errorf("read body: %w", err)
}
return body, nil
}
// rpcClient manages JSON-RPC request IDs and pending responses.
type rpcClient struct {
writer *lspWriter
nextID atomic.Int64
pendingMu sync.Mutex
pending map[int64]chan *jsonRPCResponse
}
func newRPCClient(w io.Writer) *rpcClient {
c := &rpcClient{
writer: newLSPWriter(w),
pending: make(map[int64]chan *jsonRPCResponse),
}View on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the child process state — check for a crash (see stderr buffer / exit status) that truncated the message
- Fix the writer so Content-Length exactly matches the body byte count
- Retry the session: truncation usually means the child is gone and the session must be restarted
- For mocks, build messages with a helper computing len(body) instead of hard-coded lengths
Example fix
// before: header/body mismatch truncates the reader fmt.Fprintf(w, "Content-Length: 42\r\n\r\n%s", shortBody) // after fmt.Fprintf(w, "Content-Length: %d\r\n\r\n%s", len(body), body)
Defensive patterns
Strategy: try-catch
Try / catch
body, err := readMessage(lr)
if err != nil {
var eofLike = errors.Is(err, io.ErrUnexpectedEOF) || strings.Contains(err.Error(), "read body")
if eofLike {
slog.Warn("copilot: message truncated, child likely died — restarting session")
return errSessionDead
}
return err
} Prevention
- Monitor the child process exit status alongside the reader to distinguish crash vs framing bug
- Capture stderr to diagnose truncation causes
- Make mock writers compute body length programmatically
- Restart the session on truncation; mid-message death is unrecoverable
When it happens
Trigger: The copilot process dies or closes stdout after sending the header but before/within the body; the declared Content-Length is larger than the actual bytes written; the pipe is broken mid-read; a mock stream terminates early.
Common situations: Copilot CLI crash or OOM kill mid-message; network-like stream truncation with stdio when the child exits; a test mock that writes a header with length N but only M<N bytes; header says 100 but body contains trailing data missing bytes.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/42859b17739e38f0.
Report an issue: GitHub.