{"record":{"id":"edb5a6b8780389c4","repo":"caddyserver/caddy","slug":"bad-response-status-code-from-http-loader-url-v","errorCode":null,"errorMessage":"bad response status code from http loader url: %v","messagePattern":"bad response status code from http loader url: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/httploader.go","lineNumber":144,"sourceCode":"\t}\n\tresult, warnings, err := adaptByContentType(ct, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, warn := range warnings {\n\t\tctx.Logger().Warn(warn.String())\n\t}\n\n\treturn result, nil\n}\n\nfunc attemptHttpCall(client *http.Client, request *http.Request) (*http.Response, error) {\n\tresp, err := client.Do(request) //nolint:gosec // no SSRF; comes from trusted config\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem calling http loader url: %v\", err)\n\t} else if resp.StatusCode < 200 || resp.StatusCode > 499 {\n\t\tresp.Body.Close()\n\t\treturn nil, fmt.Errorf(\"bad response status code from http loader url: %v\", resp.StatusCode)\n\t}\n\treturn resp, nil\n}\n\nfunc doHttpCallWithRetries(ctx caddy.Context, client *http.Client, request *http.Request) (*http.Response, error) {\n\tvar resp *http.Response\n\tvar err error\n\tconst maxAttempts = 10\n\n\tfor i := range maxAttempts {\n\t\tresp, err = attemptHttpCall(client, request)\n\t\tif err != nil && i < maxAttempts-1 {\n\t\t\tselect {\n\t\t\tcase <-time.After(time.Millisecond * 500):\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn resp, ctx.Err()\n\t\t\t}\n\t\t} else {","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/httploader.go#L126-L162","documentation":"attemptHttpCall accepts only 2xx-4xx as deliverable responses; anything below 200 or above 499 is closed and reported with this message. doHttpCallWithRetries then retries up to maxAttempts (10), so this error surfaces to Load only when the endpoint persistently returns 5xx (or an odd final 1xx) across all attempts.","triggerScenarios":"Config endpoint persistently returning 500/502/503 — crashing config service, bad gateway behind a proxy whose upstream is down — for 10 consecutive attempts.","commonSituations":"Config service behind a load balancer whose upstream is down; the service erroring while rendering the config; chronic upstream instability.","solutions":["Check the config service health and logs; fix the server-side error it is returning.","curl the URL to confirm the status; a proxy 502 usually means the upstream is down.","Keep the endpoint returning 2xx for success; chronic 5xx will not be saved by the built-in 10 retries — fix availability instead of tuning retries."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"curl -s -o /dev/null -w '%{http_code}' \"$CONFIG_URL\"  # should be 2xx; investigate 5xx upstream","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Monitor the config service for chronic 5xx; Caddy's built-in 10 attempts only cover blips.","Make the endpoint idempotent and cheap so it never degrades into 502/503."],"tags":["config-loader","http","status-code","server-error"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}