{"record":{"id":"e407f365a9c38d55","repo":"github/github-mcp-server","slug":"failed-to-read-log-content-w","errorCode":null,"errorMessage":"failed to read log content: %w","messagePattern":"failed to read log content: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/buffer/buffer.go","lineNumber":113,"sourceCode":"\t\t\t\tnewlineIdx := bytes.IndexByte(chunk, '\\n')\n\t\t\t\tif newlineIdx < 0 {\n\t\t\t\t\taccumulate(chunk)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\taccumulate(chunk[:newlineIdx])\n\t\t\t\tstoreLine()\n\t\t\t\tchunk = chunk[newlineIdx+1:]\n\t\t\t}\n\t\t}\n\n\t\tif err == io.EOF {\n\t\t\tif currentLine.Len() > 0 {\n\t\t\t\tstoreLine()\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\", 0, httpResp, fmt.Errorf(\"failed to read log content: %w\", err)\n\t\t}\n\t}\n\n\tvar result []string\n\tlinesInBuffer := min(totalLines, maxJobLogLines)\n\n\tstartIndex := 0\n\tif totalLines > maxJobLogLines {\n\t\tstartIndex = writeIndex\n\t}\n\n\tfor i := range linesInBuffer {\n\t\tidx := (startIndex + i) % maxJobLogLines\n\t\tif validLines[idx] {\n\t\t\tresult = append(result, lines[idx])\n\t\t}\n\t}\n","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/buffer/buffer.go#L95-L131","documentation":"Wrapped error returned by buffer.ProcessResponseAsRingBufferToEnd when httpResp.Body.Read returns a non-EOF error while the ring buffer streams a GitHub Actions job log line by line (pkg/buffer/buffer.go:113). The %w preserves the underlying transport error, so the real cause (connection reset, TLS failure, context cancellation) stays inspectable. io.EOF is handled separately as normal termination, so this path means the download genuinely broke mid-stream.","triggerScenarios":"Calling the action_job_logs tool with return_content=true: GetWorkflowJobLogs yields a signed blob URL, downloadLogContent performs http.Get, and ProcessResponseAsRingBufferToEnd reads the body. It fails when the connection is reset mid-download, a proxy/CDN idle timeout fires on a large log, a TLS error occurs, or the caller's context is cancelled while body.Read is blocked.","commonSituations":"Very large workflow logs (tens of MB) that exceed proxy timeouts; corporate proxies or firewalls killing long-lived downloads from objects.githubusercontent.com; flaky CI networks; user aborting the MCP tool call mid-read.","solutions":["Retry the tool call - each attempt fetches a fresh signed log URL, and transient body-read breaks usually clear on the second attempt","Pass a smaller tail_lines so the transfer is shorter and less likely to be cut off","Check egress to objects.githubusercontent.com if failures are consistent (proxy, firewall, TLS interception)","Unwrap the error: errors.Is(err, context.Canceled) or context.DeadlineExceeded means the client aborted, not GitHub"],"exampleFix":"// before\ncontent, total, resp, err := buffer.ProcessResponseAsRingBufferToEnd(httpResp, tailLines)\nif err != nil {\n    return \"\", 0, resp, err\n}\n\n// after - separate caller aborts from transient failures\ncontent, total, resp, err := buffer.ProcessResponseAsRingBufferToEnd(httpResp, tailLines)\nif err != nil {\n    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n        return \"\", 0, resp, err // caller aborted: do not retry\n    }\n    return \"\", 0, resp, fmt.Errorf(\"transient log read failure, retry recommended: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// Before requesting log content: ensure the caller context is live and the body exists\nif ctx.Err() != nil {\n    return ctx.Err() // abort early instead of failing mid-read\n}\nif httpResp == nil || httpResp.Body == nil {\n    return errors.New(\"no response body to read\")\n}","typeGuard":null,"tryCatchPattern":"if _, _, _, err := buffer.ProcessResponseAsRingBufferToEnd(httpResp, n); err != nil {\n    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n        return err // caller abort: never retry\n    }\n    // transient body failure: re-fetch a fresh log URL and retry once\n    return fetchAndBufferWithRetry(ctx, client, owner, repo, jobID, n, 1)\n}","preventionTips":["Pass modest tail_lines values so log downloads finish before proxy idle timeouts","Do not cancel tool calls mid-download unless you accept partial failures","Keep egress to objects.githubusercontent.com unfiltered","Always unwrap: distinguish transport aborts from caller cancellation before retrying"],"tags":["network","streaming","io","ring-buffer","go"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}