charmbracelet/glow · error
unable to read http response body: %w
Error message
unable to read http response body: %w
What it means
After the API responds, glow reads the whole body with io.ReadAll(res.Body); this error wraps a failure or interruption during that read. The connection was established and a response started, but the body transfer broke partway. Subsequent code (JSON parse, status check) never runs.
Source
Thrown at github.go:35
return nil, fmt.Errorf("invalid url: %s", u.String())
}
type readme struct {
DownloadURL string `json:"download_url"`
}
apiURL := fmt.Sprintf("https://api.%s/repos/%s/%s/readme", u.Hostname(), owner, repo)
//nolint:bodyclose
// it is closed on the caller
res, err := http.Get(apiURL) //nolint: gosec,noctx
if err != nil {
return nil, fmt.Errorf("unable to get url: %w", err)
}
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("unable to read http response body: %w", err)
}
var result readme
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("unable to parse json: %w", err)
}
if res.StatusCode == http.StatusOK {
//nolint:bodyclose
// it is closed on the caller
resp, err := http.Get(result.DownloadURL) //nolint: noctx
if err != nil {
return nil, fmt.Errorf("unable to get url: %w", err)
}
if resp.StatusCode == http.StatusOK {
return &source{resp.Body, result.DownloadURL}, nil
}View on GitHub (pinned to e3970c813d)
Solutions
- Retry — transient mid-body resets usually clear on a second attempt
- If persistent, capture where it dies: curl --raw -o /dev/null -w '%{size_download}' https://api.github.com/...
- Bypass the suspect intermediary (different network, disable VPN, direct DNS) to identify the cutter
- Fall back to the raw file URL or a local copy so the API body is not needed
Example fix
# transient: just retry glow https://github.com/owner/repo || glow https://github.com/owner/repo # if persistent, bypass the API body glow https://raw.githubusercontent.com/owner/repo/main/README.md
Defensive patterns
Strategy: retry
Try / catch
body, err := io.ReadAll(res.Body)
if err != nil {
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() || errors.Is(err, syscall.ECONNRESET) {
// mid-body transport failure: safe to retry the whole request idempotently
for i := 0; i < 3; i++ {
res2, err2 := http.Get(apiURL)
if err2 == nil {
body, err = io.ReadAll(res2.Body)
_ = res2.Body.Close()
if err == nil {
break
}
}
time.Sleep(time.Duration(i+1) * 250 * time.Millisecond)
}
}
if err != nil {
return nil, fmt.Errorf("unable to read http response body: %w", err)
}
} Prevention
- Treat mid-body read failures as transient: retry the full GET rather than parsing partial bytes
- Avoid networks/VPNs known to cut long-lived response bodies, or fetch the raw README instead
- Set an explicit client timeout so hangs surface as errors you already handle
When it happens
Trigger: Connection reset by peer or proxy mid-body; response truncated by a flaky NAT/VPN; keep-alive race where the server closes the connection during the read; captive portals that accept the handshake then cut the stream; timeouts enforced by an intermediary.
Common situations: Unstable Wi-Fi/VPN links; aggressive corporate proxies with short body-read timeouts; cloud NAT dropping long-lived sockets; intermittent ISP interception of GitHub traffic.
Related errors
- unable to read http response body: %w
- can't find README in GitHub repository
- can't find README in GitLab repository
- unable to get url: %w
- unable to get url: %w
AI-assisted analysis of charmbracelet/glow@e3970c813d (2026-08-15).
Data as JSON: /api/errors/8399a789e469bc22.
Report an issue: GitHub.