{"record":{"id":"732ada5dcfda6cd3","repo":"github/github-mcp-server","slug":"failed-to-read-response-body-w-732ada","errorCode":null,"errorMessage":"failed to read response body: %w","messagePattern":"failed to read response body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/github/secret_scanning.go","lineNumber":83,"sourceCode":"\t\t\tclient, err := deps.GetClient(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"failed to get GitHub client: %w\", err)\n\t\t\t}\n\n\t\t\talert, resp, err := client.SecretScanning.GetAlert(ctx, owner, repo, int64(alertNumber))\n\t\t\tif err != nil {\n\t\t\t\treturn ghErrors.NewGitHubAPIErrorResponse(ctx,\n\t\t\t\t\tfmt.Sprintf(\"failed to get alert with number '%d'\", alertNumber),\n\t\t\t\t\tresp,\n\t\t\t\t\terr,\n\t\t\t\t), nil, nil\n\t\t\t}\n\t\t\tdefer func() { _ = resp.Body.Close() }()\n\n\t\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t\tbody, err := io.ReadAll(resp.Body)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"failed to read response body: %w\", err)\n\t\t\t\t}\n\t\t\t\treturn ghErrors.NewGitHubAPIStatusErrorResponse(ctx, \"failed to get alert\", resp, body), nil, nil\n\t\t\t}\n\n\t\t\tr, err := json.Marshal(alert)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"failed to marshal alert: %w\", err)\n\t\t\t}\n\n\t\t\tresult := utils.NewToolResultText(string(r))\n\t\t\t// Secret scanning alerts are access-restricted regardless of repo\n\t\t\t// visibility and surface the matched secret material itself, so the\n\t\t\t// label is always private-untrusted.\n\t\t\tresult = attachStaticIFCLabel(ctx, deps, result, ifc.LabelSecurityAlert())\n\t\t\treturn result, nil, nil\n\t\t},\n\t)\n}","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/github/secret_scanning.go#L65-L101","documentation":"get_secret_scanning_alert received a non-200 from the secret scanning API, and reading that error body with io.ReadAll failed. The original status (404 unknown alert number, 403 missing security_events scope or secret scanning disabled, 429) is masked by this secondary network failure. The error body is tiny, so a read failure means the connection broke or was cancelled right after headers.","triggerScenarios":"SecretScanning.GetAlert returns non-2xx and the stream resets before the body completes; client context cancelled mid-response; proxies tearing down error responses immediately.","commonSituations":"Context deadlines almost equal to the API latency; flaky egress networks in CI; rare in steady-state operation.","solutions":["Retry the tool call once - both the masked status and the body-read failure are usually transient","If repeatable, increase the per-request context deadline so it outlives the full response","Patch the handler to fall back to resp.StatusCode when the body is unreadable, so 404/403 are still distinguishable"],"exampleFix":"// after\nreturn nil, nil, fmt.Errorf(\"failed to get alert: status %d (body unreadable: %w)\", resp.StatusCode, err)","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isBodyReadFailure(err error) bool {\n\tvar netErr net.Error\n\treturn errors.As(err, &netErr) || errors.Is(err, io.ErrUnexpectedEOF)\n}","tryCatchPattern":"result, extra, err := callTool(ctx, \"get_secret_scanning_alert\", args)\nif err != nil && strings.Contains(err.Error(), \"failed to read response body\") {\n    // the real HTTP status was masked; retry once\n    result, extra, err = callTool(ctx, \"get_secret_scanning_alert\", args)\n}\nif err != nil {\n    return fmt.Errorf(\"get_secret_scanning_alert failed (status masked): %w\", err)\n}","preventionTips":["Set per-request deadlines that outlive full response bodies, not just headers","Retry once on body-read failures before reporting - the masked status is often transient","Patch the handler to emit resp.StatusCode alongside the read error"],"tags":["go","mcp","network","io","secret-scanning","http"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}