gohugoio/hugo · error
failed to fetch remote resource from
Error message
failed to fetch remote resource from '%s': %s
What it means
Returned by `Client.FromRemote` (remote.go:285-287) when the remote server responds with a status outside 200-299 (and not 404, which is treated as nil/miss). The error is wrapped in an `*HTTPError` (via `toHTTPError`) carrying `StatusCode`, `Status`, `Headers`, and (for non-HEAD) the response body as `Data` -- useful for diagnostics. Note: this is the single-request failure; the retry loop at the transport layer (error 598) handles transient codes before this is reached.
Solutions
- Inspect the `HTTPError.Data.StatusCode` and `Body` to identify the server's complaint.
- For 401/403, provide correct auth in `headers` (`Authorization`, cookies, API key).
- For 429/5xx, raise the retry timeout via `http.timeout` config or per-call `timeout`, and check `Retry-After` handling.
- For 404 that should be tolerated, handle nil return (Hugo returns nil for 404 already); for other 4xx, fix the URL/method/headers.
- Verify the endpoint with `curl -i` from the build host to rule out network/firewall blocks.
Example fix
// template -- before
{{ $r := resources.GetRemote $url }}
// after
{{ $r := resources.GetRemote $url (dict "headers" (dict "Authorization" "Bearer $TOKEN")) }} Defensive patterns
Strategy: try-catch
Validate before calling
// Reachability + auth pre-check.
import (
"net/http"
"fmt"
)
func probe(url string, headers map[string]string) error {
req, _ := http.NewRequest("GET", url, nil)
for k, v := range headers { req.Header.Add(k, v) }
resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return fmt.Errorf("upstream returned %d", resp.StatusCode)
}
return nil
} Try / catch
// Inspect HTTPError.Data for actionable codes.
if he, ok := err.(*create.HTTPError); ok {
switch he.Data["StatusCode"] {
case 401, 403:
return fmt.Errorf("auth failed: %v", he.Data["Body"])
case 429:
return fmt.Errorf("rate limited; retry later")
}
} Prevention
- Provide auth headers for protected endpoints.
- Pre-probe the endpoint with curl from the build host.
- Tune `httpTimeout` for slow/rate-limited providers.
- Handle non-transient 4xx by fixing inputs, not retrying.
When it happens
Trigger: `resources.GetRemote` returns this for any 4xx/5xx after retries are exhausted (or for non-transient codes like 401/403/410 that are never retried). The error includes the URL and `http.StatusText` of the code.
Common situations: 401/403 from missing or expired auth token in `headers`; 410 from a deprecated endpoint; 5xx that the retry budget couldn't resolve; CORS/CDN returning 403 for blocked User-Agent; rate-limit 429 that exceeded `httpcache.timeout`; wrong path 404-vs-410 mix-up; the server requires a header the request didn't send.
Related errors
- retry timeout (configured to ) fetching remote resource
- failed to read remote resource
- failed to create request for resource
- failed to parse URL for resource
- failed to calculate hash
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/73edc44a7de603b1.
Report an issue: GitHub.
Appendix: source
Thrown at resources/resource_factories/create/remote.go:286
if cancel != nil {
defer cancel()
}
if err != nil {
return nil, err
}
defer res.Body.Close()
c.configurePollingIfEnabled(uri, optionsKey, getRes)
if res.StatusCode == http.StatusNotFound {
// Not found. This matches how lookups for local resources work.
// 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 {View on GitHub (pinned to 52c9bd7908)