{"record":{"id":"73edc44a7de603b1","repo":"gohugoio/hugo","slug":"failed-to-fetch-remote-resource-from-s-s","errorCode":null,"errorMessage":"failed to fetch remote resource from '%s': %s","messagePattern":"failed to fetch remote resource from '(.+?)': (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"resources/resource_factories/create/remote.go","lineNumber":286,"sourceCode":"\t\tif cancel != nil {\n\t\t\tdefer cancel()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer res.Body.Close()\n\n\t\tc.configurePollingIfEnabled(uri, optionsKey, getRes)\n\n\t\tif res.StatusCode == http.StatusNotFound {\n\t\t\t// Not found. This matches how lookups for local resources work.\n\t\t\t// To cache this, we need to make sure the body is read.\n\t\t\tio.Copy(io.Discard, res.Body)\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tif res.StatusCode < 200 || res.StatusCode > 299 {\n\t\t\treturn nil, toHTTPError(fmt.Errorf(\"failed to fetch remote resource from '%s': %s\", uri, http.StatusText(res.StatusCode)), res, !isHeadMethod, options.ResponseHeaders)\n\t\t}\n\n\t\tvar (\n\t\t\tbody      []byte\n\t\t\tmediaType media.Type\n\t\t)\n\t\t// A response to a HEAD method should not have a body. If it has one anyway, that body must be ignored.\n\t\t// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD\n\t\tif !isHeadMethod && res.Body != nil {\n\t\t\tbody, err = io.ReadAll(res.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to read remote resource %q: %w\", uri, err)\n\t\t\t}\n\t\t}\n\n\t\tfilename := path.Base(rURL.Path)\n\t\tif _, params, _ := mime.ParseMediaType(res.Header.Get(\"Content-Disposition\")); params != nil {\n\t\t\tif _, ok := params[\"filename\"]; ok {","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/resources/resource_factories/create/remote.go#L268-L304","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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."],"exampleFix":"// template -- before\n{{ $r := resources.GetRemote $url }}\n\n// after\n{{ $r := resources.GetRemote $url (dict \"headers\" (dict \"Authorization\" \"Bearer $TOKEN\")) }}","handlingStrategy":"try-catch","validationCode":"// Reachability + auth pre-check.\nimport (\n\t\"net/http\"\n\t\"fmt\"\n)\nfunc probe(url string, headers map[string]string) error {\n\treq, _ := http.NewRequest(\"GET\", url, nil)\n\tfor k, v := range headers { req.Header.Add(k, v) }\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil { return err }\n\tdefer resp.Body.Close()\n\tif resp.StatusCode < 200 || resp.StatusCode > 299 {\n\t\treturn fmt.Errorf(\"upstream returned %d\", resp.StatusCode)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"// Inspect HTTPError.Data for actionable codes.\nif he, ok := err.(*create.HTTPError); ok {\n\tswitch he.Data[\"StatusCode\"] {\n\tcase 401, 403:\n\t\treturn fmt.Errorf(\"auth failed: %v\", he.Data[\"Body\"])\n\tcase 429:\n\t\treturn fmt.Errorf(\"rate limited; retry later\")\n\t}\n}","preventionTips":["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."],"tags":["resources","remote","http","network","retry"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}