chenhg5/cc-connect · error

write body: %w

Error message

write body: %w

What it means

After the header was written successfully, writing the JSON-RPC message body bytes to the copilot process's stdin pipe failed. Like the header write failure, this indicates the child process's stdin is closed or broken — typically the copilot process died between the header and body write or mid-request.

Source

Thrown at agent/copilot/jsonrpc.go:73

	return &lspWriter{w: w}
}

func (lw *lspWriter) writeMessage(v any) error {
	data, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("marshal: %w", err)
	}

	header := fmt.Sprintf("Content-Length: %d\r\n\r\n", len(data))

	lw.mu.Lock()
	defer lw.mu.Unlock()

	if _, err := io.WriteString(lw.w, header); err != nil {
		return fmt.Errorf("write header: %w", err)
	}
	if _, err := lw.w.Write(data); err != nil {
		return fmt.Errorf("write body: %w", err)
	}
	return nil
}

// lspReader reads Content-Length framed JSON-RPC messages.
type lspReader struct {
	reader *bufio.Reader
}

func newLSPReader(r io.Reader) *lspReader {
	return &lspReader{reader: bufio.NewReaderSize(r, 64*1024)}
}

// readMessage reads one Content-Length framed message and returns the raw JSON body.
func (lr *lspReader) readMessage() ([]byte, error) {
	contentLength := -1
	for {
		line, err := lr.reader.ReadString('\n')

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Capture copilot stderr (replace io.Discard) to find why the process died
  2. Retry the operation on a freshly created probe session
  3. Check message size; ensure the reader goroutine is draining stdout so the pipe does not block
  4. Verify ctx timeouts are generous enough for the RPC being sent

Example fix

// before
if _, err := lw.w.Write(data); err != nil {
    return fmt.Errorf("write body: %w", err)
}
// after
if _, err := lw.w.Write(data); err != nil {
    return fmt.Errorf("write body (len=%d): %w", len(data), err)
} // extra context for diagnosing dead-process writes
Defensive patterns

Strategy: retry

Validate before calling

// preflight liveness + reasonable payload size
if closed := func() bool { select { case <-done: return true; default: return false } }(); closed {
    return errors.New("probe process exited")
}
if len(payload) > 1<<20 { return errors.New("rpc payload unusually large") }

Try / catch

if err := rpc.call(...); err != nil && strings.Contains(err.Error(), "write body") {
    slog.Warn("rpc write failed mid-message; process likely died", "err", err)
    // recreate session and retry
}

Prevention

When it happens

Trigger: call/notify/writeResponse/writeError issuing lw.w.Write(data) on a dead copilot process's stdin pipe; large messages hitting a full pipe buffer with a dead reader.

Common situations: Copilot CLI crash under memory pressure; context timeout killing the process during a large request write; race between process teardown and a pending RPC.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/0234f5bf6bcdd5c0. Report an issue: GitHub.