{"record":{"id":"e384f5baaefab42d","repo":"chenhg5/cc-connect","slug":"missing-content-length-header","errorCode":null,"errorMessage":"missing Content-Length header","messagePattern":"missing Content-Length header","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/copilot/jsonrpc.go","lineNumber":112,"sourceCode":"\t\t}\n\t\tline = strings.TrimRight(line, \"\\r\\n\")\n\t\tif line == \"\" {\n\t\t\t// End of headers\n\t\t\tbreak\n\t\t}\n\t\tif strings.HasPrefix(line, \"Content-Length: \") {\n\t\t\tval := strings.TrimPrefix(line, \"Content-Length: \")\n\t\t\tn, err := strconv.Atoi(strings.TrimSpace(val))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid Content-Length: %w\", err)\n\t\t\t}\n\t\t\tcontentLength = n\n\t\t}\n\t\t// Ignore other headers (Content-Type, etc.)\n\t}\n\n\tif contentLength < 0 {\n\t\treturn nil, fmt.Errorf(\"missing Content-Length header\")\n\t}\n\tif contentLength > 10*1024*1024 {\n\t\treturn nil, fmt.Errorf(\"Content-Length too large: %d\", contentLength)\n\t}\n\n\tbody := make([]byte, contentLength)\n\tif _, err := io.ReadFull(lr.reader, body); err != nil {\n\t\treturn nil, fmt.Errorf(\"read body: %w\", err)\n\t}\n\treturn body, nil\n}\n\n// rpcClient manages JSON-RPC request IDs and pending responses.\ntype rpcClient struct {\n\twriter *lspWriter\n\tnextID atomic.Int64\n\n\tpendingMu sync.Mutex","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/copilot/jsonrpc.go#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: raw JSON written to the pipe\nfmt.Fprintf(w, `{\"jsonrpc\":\"2.0\",\"result\":{},\"id\":1}\\n`)\n// after: LSP-framed message\nbody := `{\"jsonrpc\":\"2.0\",\"result\":{},\"id\":1}`\nfmt.Fprintf(w, \"Content-Length: %d\\r\\n\\r\\n%s\", len(body), body)","handlingStrategy":"validation","validationCode":"func isLSPFramed(stream []byte) bool {\n    return bytes.HasPrefix(stream, []byte(\"Content-Length:\"))\n}","typeGuard":"func hasContentLength(header string) bool {\n    return strings.Contains(strings.ToLower(header), \"content-length:\")\n}","tryCatchPattern":"body, err := readMessage(lr)\nif err != nil {\n    if strings.Contains(err.Error(), \"missing Content-Length\") {\n        slog.Warn(\"copilot: non-LSP output on stdout, draining\", \"err\", err)\n        return errRecoverable\n    }\n    return err\n}","preventionTips":["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"],"tags":["jsonrpc","lsp","framing","io"],"backgroundTag":"missing-required-header","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}