{"record":{"id":"f870a6ff421ddb71","repo":"caddyserver/caddy","slug":"problem-calling-http-loader-url-v","errorCode":null,"errorMessage":"problem calling http loader url: %v","messagePattern":"problem calling http loader url: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/httploader.go","lineNumber":141,"sourceCode":"\tct := resp.Header.Get(\"Content-Type\")\n\tif hl.Adapter != \"\" {\n\t\tct = \"text/\" + hl.Adapter\n\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():","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/httploader.go#L123-L159","documentation":"The lowest-level transport failure inside attemptHttpCall: http.Client.Do itself errored (DNS failure, connection refused, TLS handshake error, timeout). It is wrapped with this message and — unless it was the 10th and final attempt — retried by doHttpCallWithRetries; the error escapes only after all retries are exhausted or the context is done.","triggerScenarios":"Config URL host not resolving; config server not listening (connection refused); TLS certificate of the config endpoint untrusted by the client's pool; the loader's Timeout too short for a slow endpoint.","commonSituations":"Boot-order races where Caddy starts before the config service; self-signed TLS on the config server without RootCAPEMFiles configured; restrictive DNS/network policy in containers.","solutions":["Verify reachability from the Caddy host: 'curl -v <config-url>'.","For self-signed/private-CA endpoints, add 'root_ca' files (and client cert/key if mTLS) to the loader's tls block.","Increase the loader 'timeout' if the config endpoint is slow.","Fix boot ordering / add a startup dependency so the config service is up first."],"exampleFix":"# before\nhttp https://cfg.internal/config.json\n\n# after\nhttp https://cfg.internal/config.json {\n  timeout 60s\n  tls {\n    root_ca /etc/caddy/cfg-ca.pem\n  }\n}","handlingStrategy":"retry","validationCode":"curl -v --connect-timeout 5 \"$CONFIG_URL\" -o /dev/null  # verify DNS, TCP, TLS all work from the Caddy host","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the config service is up before Caddy starts (init/compose depends_on).","Configure root_ca/client certs for private TLS endpoints; raise the loader timeout for slow endpoints."],"tags":["config-loader","http","network","tls","dns"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}