caddyserver/caddy · error
HTTP %d: reading error message: %v
Error message
HTTP %d: reading error message: %v
What it means
The admin endpoint answered with HTTP >= 400, but while reading the (capped at 2 MiB) error response body the read itself failed. This is a secondary failure: the transport broke mid-response, so the CLI can report neither the status body nor succeed.
Source
Thrown at cmd/commandfuncs.go:848
// expects; reuse is not of particular concern here
client := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial(parsedAddr.Network, parsedAddr.JoinHostPort(0))
},
},
}
resp, err := client.Do(req) //nolint:gosec // the only SSRF here would be self-sabotage I think
if err != nil {
return nil, fmt.Errorf("performing request: %v", err)
}
// if it didn't work, let the user know
if resp.StatusCode >= 400 {
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 1024*1024*2))
if err != nil {
return nil, fmt.Errorf("HTTP %d: reading error message: %v", resp.StatusCode, err)
}
return nil, fmt.Errorf("caddy responded with error: HTTP %d: %s", resp.StatusCode, respBody)
}
return resp, nil
}
// DetermineAdminAPIAddress determines which admin API endpoint address should
// be used based on the inputs. By priority: if `address` is specified, then
// it is returned; if `config` is specified, then that config will be used for
// finding the admin address; if `configFile` (and `configAdapter`) are specified,
// then that config will be loaded to find the admin address; otherwise, the
// default admin listen address will be returned.
func DetermineAdminAPIAddress(address string, config []byte, configFile, configAdapter string) (string, error) {
// Prefer the address if specified and non-empty
if address != "" {
return address, nil
}View on GitHub (pinned to 50e54ee279)
Solutions
- Retry the CLI command once the daemon is stable
- Check for two processes fighting over the same admin endpoint
- If reproducible, capture daemon logs around the request to find why the body stream was cut
Defensive patterns
Strategy: retry
Try / catch
if err != nil && strings.Contains(err.Error(), "reading error message") { time.Sleep(time.Second); err = retryCmd() } Prevention
- Avoid issuing admin API calls while restarting the daemon
- One writer at a time: serialize config-change scripts
When it happens
Trigger: The admin connection is severed after headers are sent — daemon killed mid-request, unix socket removed during the call, or a proxy in between closing the stream.
Common situations: Rare in practice; seen when the Caddy process is being torn down concurrently with a CLI call, or when something between the CLI and the daemon truncates responses.
Related errors
- encoding config: %v
- invalid admin address %s: %v
- making request: %v
- performing request: %v
- caddy responded with error: HTTP %d: %s
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/022fce5daadb5754.
Report an issue: GitHub.