GoogleContainerTools/skaffold · error
http %d, error %q
Error message
http %d, error %q
What it means
Download requires an HTTP 200 response; any other status code produces this error with the numeric code and the status text (e.g. 'http 404, error "404 Not Found"'). It is a guard against treating error pages or redirects-to-error bodies as valid payloads.
Source
Thrown at pkg/skaffold/util/http.go:40
"net/http"
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/version"
)
func Download(url string) ([]byte, error) {
client := http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("creating http request: %w", err)
}
req.Header.Set("User-Agent", version.UserAgentWithClient())
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http %d, error %q", resp.StatusCode, resp.Status)
}
return io.ReadAll(resp.Body)
}
View on GitHub (pinned to a1189de023)
Solutions
- Check the status code in the message: 404 means wrong/removed URL; 403 means access denied; 429 means back off and retry.
- Verify the URL is still valid by curling it and comparing status.
- For GCS, confirm the object exists and the bucket is publicly readable (or use authenticated access).
- Add retry with backoff for transient 429/5xx before treating it as fatal.
- If a proxy interferes, allowlist the host or fix proxy credentials.
Example fix
// before: one-shot download, fails hard on 429
b, err := util.Download(remoteCfgURL)
// after: retry transient statuses
var b []byte
for i := 0; i < 3; i++ {
b, err = util.Download(remoteCfgURL)
if err == nil || !strings.Contains(fmt.Sprint(err), "http 4") || !strings.Contains(fmt.Sprint(err), "429") {
break
}
time.Sleep(time.Duration(1<<i) * time.Second)
} Defensive patterns
Strategy: retry
Validate before calling
// can't pre-validate server status, but can pre-check reachability
resp, err := http.Head(url)
if err == nil && resp.StatusCode >= 400 {
return fmt.Errorf("URL returns %d before download", resp.StatusCode)
} Type guard
func isHTTPStatusError(err error) bool { return strings.Contains(err.Error(), "http ") && strings.Contains(err.Error(), ", error ") }
func statusCodeFrom(err error) int {
var code int
fmt.Sscanf(err.Error(), "http %d", &code)
return code
} Try / catch
b, err := util.Download(url)
if err != nil {
if code := statusCodeFrom(err); code == 429 || code >= 500 {
// retry with exponential backoff
} else if code == 404 {
return fmt.Errorf("resource not found at %s — check the URL", url)
}
return err
} Prevention
- Verify the URL returns 200 with curl or a HEAD pre-check before relying on it
- Retry 429/5xx with exponential backoff; fail fast on 4xx client errors
- Confirm GCS object existence and public-read ACL for public downloads
- Keep URLs to remote configs in version control and validate them in CI
When it happens
Trigger: client.Do succeeds but resp.StatusCode != 200: 404 (file removed/moved on GCS), 403 (permissions/blocked by firewall), 429 (rate limited), 5xx (server error).
Common situations: Fetching a remote skaffold config or latest-version file whose path changed; GCS bucket made private; corporate proxies returning 403; rate limiting after many checks; typos in remote config URLs in skaffold.yaml.
Related errors
- getting latest version info from GCS: %w
- failed to iterate objects: %v
- failed to read object: %v
- failed to copy object to file: %v
- error copying file to GCS: %w
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/6500171c74f35666.
Report an issue: GitHub.