{"record":{"id":"dd864ac0e808ecf0","repo":"chenhg5/cc-connect","slug":"read-header-w","errorCode":null,"errorMessage":"read header: %w","messagePattern":"read header: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/copilot/jsonrpc.go","lineNumber":93,"sourceCode":"\treturn nil\n}\n\n// lspReader reads Content-Length framed JSON-RPC messages.\ntype lspReader struct {\n\treader *bufio.Reader\n}\n\nfunc newLSPReader(r io.Reader) *lspReader {\n\treturn &lspReader{reader: bufio.NewReaderSize(r, 64*1024)}\n}\n\n// readMessage reads one Content-Length framed message and returns the raw JSON body.\nfunc (lr *lspReader) readMessage() ([]byte, error) {\n\tcontentLength := -1\n\tfor {\n\t\tline, err := lr.reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"read header: %w\", err)\n\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 {","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/copilot/jsonrpc.go#L75-L111","documentation":"The lspReader failed while reading the header block of a Content-Length framed JSON-RPC message from the copilot process's stdout. ReadString('\\n') returned an error — almost always io.EOF because the copilot process exited/closed stdout, or io.ErrUnexpectedEOF on truncated output. This is how a dead copilot process manifests on the read side.","triggerScenarios":"readMessage invoked by the reader goroutine or tests when the copilot process terminates or its stdout closes before a full header block arrives; malformed output with no trailing newline before EOF.","commonSituations":"Copilot CLI crashing or being killed (OOM, timeout); copilot writing non-framed diagnostics to stdout and then exiting; binary exiting immediately due to bad args or missing auth.","solutions":["Check why the copilot process exited: capture stderr instead of io.Discard, inspect exit status","Verify the copilot CLI is authenticated and runs without error when invoked manually","Ensure copilot writes LSP frames to stdout and logs to stderr (a CLI printing logs to stdout breaks framing)","Restart the probe session and retry the operation"],"exampleFix":"// before\ncmd.Stderr = io.Discard // can't tell why stdout hit EOF\n// after\nvar stderr bytes.Buffer\ncmd.Stderr = &stderr\ngo func() { <-done; slog.Debug(\"copilot probe exited\", \"stderr\", stderr.String()) }()","handlingStrategy":"try-catch","validationCode":"// smoke-test the CLI's framed output before relying on it\ncmd := exec.Command(bin, args...)\nvar out, errb bytes.Buffer\ncmd.Stdout, cmd.Stderr = &out, &errb\nif err := cmd.Run(); err != nil || !bytes.HasPrefix(out, []byte(\"Content-Length: \")) {\n    return fmt.Errorf(\"copilot stdout not LSP-framed: %q stderr=%q\", out.Bytes()[:min(64,len(out))], errb.String())\n}","typeGuard":null,"tryCatchPattern":"if _, err := reader.readMessage(); err != nil {\n    if errors.Is(err, io.EOF) || strings.Contains(err.Error(), \"read header\") {\n        slog.Warn(\"copilot process closed stdout (likely exited)\", \"waitErr\", cmd.Wait())\n        // restart probe session\n    }\n}","preventionTips":["Never let the CLI or wrapper scripts print logs to stdout; stderr only","Capture stderr instead of io.Discard in debug builds","Authenticate the CLI beforehand (copilot auth) so it does not exit immediately"],"tags":["io","jsonrpc","process-exit","eof"],"backgroundTag":"process-exited-unexpectedly","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}