gohugoio/hugo · error
failed to read remote resource
Error message
failed to read remote resource %q: %w
What it means
Wrapped error from `Client.FromRemote` (remote.go:295-299) when `io.ReadAll(res.Body)` fails after a 2xx response. This is a mid-stream read failure on the response body -- the server returned a success status but the connection dropped or errored while streaming bytes. Distinct from 595 (status-level) and 598 (retry timeout).
Solutions
- Raise the per-request `timeout` option so the read completes: `(dict "timeout" "60s")`.
- For large assets, fetch and cache locally rather than streaming through Hugo.
- Retry the build (transient mid-stream failures often clear); the retry loop only covers request establishment, not body read, so a re-run is needed.
- If persistent, fetch the URL with `curl` from the build host to isolate server-side truncation.
- Reduce body size (server-side pagination) or fetch a compressed variant.
Example fix
// template -- before
{{ $r := resources.GetRemote $url }}
// after
{{ $r := resources.GetRemote $url (dict "timeout" "120s") }} Defensive patterns
Strategy: retry
Validate before calling
// Head/probe to confirm a complete body is reachable. // (Doesn't fully prevent mid-stream drops but surfaces instability early.)
Try / catch
// Retry on read-body failures.
for i := 0; i < 3; i++ {
r, err := resources.GetRemote(url, opts)
if err == nil || !strings.Contains(err.Error(), "failed to read remote resource") {
return r, err
}
time.Sleep(time.Duration(i+1) * 2 * time.Second)
} Prevention
- Set a generous `timeout` for large payloads.
- Cache large remote assets locally between builds.
- Monitor upstream stability; fall back to a committed copy when flaky.
When it happens
Trigger: Server returns 200 but closes the connection mid-body; TLS renegotiation failure during transfer; proxy buffering timeout; client-side deadline (`context.WithTimeout`) firing mid-read; very large body exhausting memory and failing allocation.
Common situations: Flaky upstream behind a CDN that truncates large payloads; slow origin server hitting the configured `timeout` after headers; CI runner with constrained memory fetching a large asset; corporate proxy killing long streams; intermittent network blip mid-download.
Related errors
- failed to fetch remote resource from
- retry timeout (configured to ) fetching remote resource
- failed to calculate hash
- failed to create request for resource
- failed to parse URL for resource
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/45f1d3a798467783.
Report an issue: GitHub.
Appendix: source
Thrown at resources/resource_factories/create/remote.go:298
// To cache this, we need to make sure the body is read.
io.Copy(io.Discard, res.Body)
return nil, nil
}
if res.StatusCode < 200 || res.StatusCode > 299 {
return nil, toHTTPError(fmt.Errorf("failed to fetch remote resource from '%s': %s", uri, http.StatusText(res.StatusCode)), res, !isHeadMethod, options.ResponseHeaders)
}
var (
body []byte
mediaType media.Type
)
// A response to a HEAD method should not have a body. If it has one anyway, that body must be ignored.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD
if !isHeadMethod && res.Body != nil {
body, err = io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed to read remote resource %q: %w", uri, err)
}
}
filename := path.Base(rURL.Path)
if _, params, _ := mime.ParseMediaType(res.Header.Get("Content-Disposition")); params != nil {
if _, ok := params["filename"]; ok {
filename = params["filename"]
}
}
contentType := res.Header.Get("Content-Type")
// For HEAD requests we have no body to work with, so we need to use the Content-Type header.
if isHeadMethod || c.rs.ExecHelper.Sec().HTTP.MediaTypes.Accept(contentType) {
var found bool
mediaType, found = c.rs.MediaTypes().GetByType(contentType)
if !found {
// A media type not configured in Hugo, just create one from the content type string.View on GitHub (pinned to 52c9bd7908)