github/github-mcp-server · error

failed to read response body: %w

Error message

failed to read response body: %w

What it means

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.

Source

Thrown at pkg/github/secret_scanning.go:83

			client, err := deps.GetClient(ctx)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to get GitHub client: %w", err)
			}

			alert, resp, err := client.SecretScanning.GetAlert(ctx, owner, repo, int64(alertNumber))
			if err != nil {
				return ghErrors.NewGitHubAPIErrorResponse(ctx,
					fmt.Sprintf("failed to get alert with number '%d'", alertNumber),
					resp,
					err,
				), nil, nil
			}
			defer func() { _ = resp.Body.Close() }()

			if resp.StatusCode != http.StatusOK {
				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 alert", resp, body), nil, nil
			}

			r, err := json.Marshal(alert)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to marshal alert: %w", err)
			}

			result := utils.NewToolResultText(string(r))
			// Secret scanning alerts are access-restricted regardless of repo
			// visibility and surface the matched secret material itself, so the
			// label is always private-untrusted.
			result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelSecurityAlert())
			return result, nil, nil
		},
	)
}

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Retry the tool call once - both the masked status and the body-read failure are usually transient
  2. If repeatable, increase the per-request context deadline so it outlives the full response
  3. Patch the handler to fall back to resp.StatusCode when the body is unreadable, so 404/403 are still distinguishable

Example fix

// after
return nil, nil, fmt.Errorf("failed to get alert: 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

result, extra, err := callTool(ctx, "get_secret_scanning_alert", args)
if err != nil && strings.Contains(err.Error(), "failed to read response body") {
    // the real HTTP status was masked; retry once
    result, extra, err = callTool(ctx, "get_secret_scanning_alert", args)
}
if err != nil {
    return fmt.Errorf("get_secret_scanning_alert failed (status masked): %w", err)
}

Prevention

When it happens

Trigger: SecretScanning.GetAlert returns non-2xx and the stream resets before the body completes; client context cancelled mid-response; proxies tearing down error responses immediately.

Common situations: Context deadlines almost equal to the API latency; flaky egress networks in CI; rare in steady-state operation.

Related errors


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