chenhg5/cc-connect · error
missing Content-Length header
Error message
missing Content-Length header
What it means
readMessage parses the LSP-style JSON-RPC framing from the copilot child process stream. Every LSP message must begin with a `Content-Length:` header; the parser initializes contentLength to -1 and if no such header was seen before the blank line it returns "missing Content-Length header". This guards against garbage bytes, protocol desync, or a non-LSP stream coming from the child.
Source
Thrown at agent/copilot/jsonrpc.go:112
}
line = strings.TrimRight(line, "\r\n")
if line == "" {
// End of headers
break
}
if strings.HasPrefix(line, "Content-Length: ") {
val := strings.TrimPrefix(line, "Content-Length: ")
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.MutexView on GitHub (pinned to 4000b2338a)
Solutions
- Ensure every emitted message begins with a `Content-Length: <N>\r\n` header followed by a blank line before the N-byte body
- Check what the child process actually wrote to stdout (log raw bytes) to confirm it is LSP-framed
- Verify the copilot CLI version is one that speaks vscode-jsonrpc framing
- If feeding the reader in tests, use a helper that builds the header from len(body) instead of hand-writing it
Example fix
// before: raw JSON written to the pipe
fmt.Fprintf(w, `{"jsonrpc":"2.0","result":{},"id":1}\n`)
// after: LSP-framed message
body := `{"jsonrpc":"2.0","result":{},"id":1}`
fmt.Fprintf(w, "Content-Length: %d\r\n\r\n%s", len(body), body) Defensive patterns
Strategy: validation
Validate before calling
func isLSPFramed(stream []byte) bool {
return bytes.HasPrefix(stream, []byte("Content-Length:"))
} Type guard
func hasContentLength(header string) bool {
return strings.Contains(strings.ToLower(header), "content-length:")
} Try / catch
body, err := readMessage(lr)
if err != nil {
if strings.Contains(err.Error(), "missing Content-Length") {
slog.Warn("copilot: non-LSP output on stdout, draining", "err", err)
return errRecoverable
}
return err
} Prevention
- Generate test fixtures with a helper that computes Content-Length from the body
- Never let anything other than the JSON-RPC writer touch the child's stdout
- Pin and test against the copilot CLI version you deploy
- Log raw stdout bytes at debug level when framing errors occur
When it happens
Trigger: Calling readMessage (directly via LSPReader or via runMockCopilot / the reader goroutine) on a stream that produced headers without a Content-Length line — e.g. raw JSON without framing, a partial header line split without its CRLF, or headers like `Content-Type: application/vscode-jsonrpc; charset=utf-8` only.
Common situations: Mock/stdin scripts written for tests that forget the Content-Length header; the copilot CLI version changed its output framing; stderr noise accidentally piped into stdout; a manually crafted test fixture with only `\r\n` line endings mismatched.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/e384f5baaefab42d.
Report an issue: GitHub.