caddyserver/caddy · error

problem calling http loader url: %v

Error message

problem calling http loader url: %v

What it means

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.

Source

Thrown at caddyconfig/httploader.go:141

	ct := resp.Header.Get("Content-Type")
	if hl.Adapter != "" {
		ct = "text/" + hl.Adapter
	}
	result, warnings, err := adaptByContentType(ct, body)
	if err != nil {
		return nil, err
	}
	for _, warn := range warnings {
		ctx.Logger().Warn(warn.String())
	}

	return result, nil
}

func attemptHttpCall(client *http.Client, request *http.Request) (*http.Response, error) {
	resp, err := client.Do(request) //nolint:gosec // no SSRF; comes from trusted config
	if err != nil {
		return nil, fmt.Errorf("problem calling http loader url: %v", err)
	} else if resp.StatusCode < 200 || resp.StatusCode > 499 {
		resp.Body.Close()
		return nil, fmt.Errorf("bad response status code from http loader url: %v", resp.StatusCode)
	}
	return resp, nil
}

func doHttpCallWithRetries(ctx caddy.Context, client *http.Client, request *http.Request) (*http.Response, error) {
	var resp *http.Response
	var err error
	const maxAttempts = 10

	for i := range maxAttempts {
		resp, err = attemptHttpCall(client, request)
		if err != nil && i < maxAttempts-1 {
			select {
			case <-time.After(time.Millisecond * 500):
			case <-ctx.Done():

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Verify reachability from the Caddy host: 'curl -v <config-url>'.
  2. For self-signed/private-CA endpoints, add 'root_ca' files (and client cert/key if mTLS) to the loader's tls block.
  3. Increase the loader 'timeout' if the config endpoint is slow.
  4. Fix boot ordering / add a startup dependency so the config service is up first.

Example fix

# before
http https://cfg.internal/config.json

# after
http https://cfg.internal/config.json {
  timeout 60s
  tls {
    root_ca /etc/caddy/cfg-ca.pem
  }
}
Defensive patterns

Strategy: retry

Validate before calling

curl -v --connect-timeout 5 "$CONFIG_URL" -o /dev/null  # verify DNS, TCP, TLS all work from the Caddy host

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/f870a6ff421ddb71. Report an issue: GitHub.