github/github-mcp-server · error
failed to read response body: %w
Error message
failed to read response body: %w
What it means
In get_commit, client.Repositories.GetCommit returned non-200 and the follow-up io.ReadAll(resp.Body) needed for the structured status error failed. The transport read failure replaces the real GitHub status (usually 404 unknown SHA or 422/451 blocked by DMCA/policy).
Source
Thrown at pkg/github/repositories.go:111
client, err := deps.GetClient(ctx)
if err != nil {
return nil, nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
commit, resp, err := client.Repositories.GetCommit(ctx, owner, repo, sha, opts)
if err != nil {
return ghErrors.NewGitHubAPIErrorResponse(ctx,
fmt.Sprintf("failed to get commit: %s", sha),
resp,
err,
), nil, nil
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != 200 {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, nil, fmt.Errorf("failed to read response body: %w", err)
}
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get commit", resp, body), nil, nil
}
// Convert to minimal commit
minimalCommit := convertToMinimalCommit(commit, detail)
r, err := json.Marshal(minimalCommit)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal response: %w", err)
}
result := utils.NewToolResultText(string(r))
// Commit content is reachable from the repo's history; in public
// repos anyone can land it via a PR (untrusted), in private repos
// only collaborators can (trusted). Confidentiality follows repo
// visibility.
result = attachRepoVisibilityIFCLabel(ctx, deps, client, owner, repo, result, ifc.LabelCommitContents)View on GitHub (pinned to 0ea1f775a7)
Solutions
- Retry once — read failures are usually transient connection-reuse races
- Verify the SHA exists and is reachable from this repo (it must be referenced by a ref or PR)
- Check rate-limit headers on adjacent calls if 403s cluster
- Inspect the wrapped error for context.Canceled to rule out client timeouts
Example fix
// before
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, nil, fmt.Errorf("failed to read response body: %w", err)
}
// after — keep the status code visible when the body is lost
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if readErr != nil {
return nil, nil, fmt.Errorf("failed to read commit body (status %d, sha %s): %w", resp.StatusCode, sha, readErr)
} Defensive patterns
Strategy: retry
Validate before calling
func validSHA(sha string) error {
if len(sha) < 7 || len(sha) > 40 {
return fmt.Errorf("suspicious SHA length %d", len(sha))
}
for _, r := range sha {
if !strings.ContainsRune("0123456789abcdefABCDEF", r) {
return fmt.Errorf("SHA contains non-hex character %q", r)
}
}
return nil
} Try / catch
res, err := callGetCommit(ctx, owner, repo, sha, detail)
if err != nil && isTransientReadErr(err) {
res, err = callGetCommit(ctx, owner, repo, sha, detail)
} Prevention
- Validate SHA shape before calling to eliminate the most common 404
- Use full 40-char SHAs when provenance matters
- Retry only transport-level failures; 404 SHAs need input correction, not retries
When it happens
Trigger: GetCommit answers 404 (SHA typo, unreferenced object, fork without the commit), 451 (content blocked), 403 (rate limit), then the error body read breaks via connection reset, HTTP/2 stream error, or context cancellation.
Common situations: Clients passing shortened or malformed SHAs; repos affected by DMCA takediffs returning 451 with large bodies; rate-limited CI jobs where truncated error bodies coincide; proxy interruptions on GHES.
Related errors
- failed to read file content: %w
- failed to read response body: %w
- failed to read response body: %w
- failed to read response body: %w
- GitHub App authentication requires a private key: set GITHUB
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/0e3696a45a13097a.
Report an issue: GitHub.