github/github-mcp-server · error

failed to read response body: %w

Error message

failed to read response body: %w

What it means

In the repo:// resource handler, the raw content request came back with a status other than 200 or 404, and io.ReadAll of that error response body failed. The real API failure (rate limit, auth, 5xx) is masked by this secondary transport error: the message describes the failed body read, not the underlying status, which is lost entirely.

Source

Thrown at pkg/github/repository_resource.go:252

				if err := base64Encoder.Close(); err != nil {
					return nil, fmt.Errorf("failed to close base64 encoder: %w", err)
				}

				return &mcp.ReadResourceResult{
					Contents: []*mcp.ResourceContents{
						{
							URI:      request.Params.URI,
							MIMEType: mimeType,
							Blob:     buf.Bytes(),
						},
					},
				}, nil
			}
		case resp.StatusCode != http.StatusNotFound:
			// If we got a response but it is not 200 OK, we return an error
			body, err := io.ReadAll(resp.Body)
			if err != nil {
				return nil, fmt.Errorf("failed to read response body: %w", err)
			}
			return nil, fmt.Errorf("failed to fetch raw content: %s", string(body))
		default:
			// This should be unreachable because GetContents should return an error if neither file nor directory content is found.
			return nil, errors.New("404 Not Found")
		}
	}
}

// expandRepoResourceURI builds a resource URI using the appropriate URI template
// based on the provided parameters (sha, ref, or default).
func expandRepoResourceURI(owner, repo, sha, ref string, pathParts []string) (string, error) {
	baseValues := uritemplate.Values{
		"owner": uritemplate.String(owner),
		"repo":  uritemplate.String(repo),
		"path":  uritemplate.List(pathParts...),
	}

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Retry the resource read - the masked status was likely transient (429/5xx) and the body-read failure is a network blip
  2. If it repeats, patch the handler to include resp.StatusCode in the error so the real status is never lost
  3. Verify token validity and rate-limit state when the pattern recurs on every call

Example fix

// before
body, err := io.ReadAll(resp.Body)
if err != nil {
    return nil, fmt.Errorf("failed to read response body: %w", err)
}
return nil, fmt.Errorf("failed to fetch raw content: %s", string(body))
// after - never lose the status code
return nil, fmt.Errorf("failed to fetch raw content: status %d (body unreadable: %w)", resp.StatusCode, err)
Defensive patterns

Strategy: retry

Type guard

func isBodyReadFailure(err error) bool {
	var netErr net.Error
	return errors.As(err, &netErr) || errors.Is(err, io.ErrUnexpectedEOF)
}

Try / catch

err := readResource(ctx, uri)
if err != nil {
    if isBodyReadFailure(err) {
        // secondary transport failure masked the real HTTP status; retry
        time.Sleep(300 * time.Millisecond)
        err = readResource(ctx, uri)
    }
    if err != nil {
        return fmt.Errorf("repo resource read failed (status masked by body read error): %w", err)
    }
}

Prevention

When it happens

Trigger: GetRawContent returns 403/429/5xx and the connection breaks or the context deadline expires while streaming even the small error body; client timeouts set so tight the error body cannot be fully read.

Common situations: Flaky networks where resets hit right after headers; servers under load where GitHub sends 5xx slowly; hard per-request timeouts in front of the MCP server.

Related errors


AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15). Data as JSON: /api/errors/bb3e36deb99677a3. Report an issue: GitHub.