caddyserver/caddy · error

performing request: %v

Error message

performing request: %v

What it means

Wraps the error from client.Do in AdminAPIRequest: the CLI could not complete the HTTP round-trip to the admin endpoint. Typical causes are connection refused (Caddy not running or admin disabled), timeouts, or dialing a unix socket path that does not exist or has wrong permissions.

Source

Thrown at cmd/commandfuncs.go:841

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	maps.Copy(req.Header, headers)

	// make an HTTP client that dials our network type, since admin
	// endpoints aren't always TCP, which is what the default transport
	// 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,

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Verify Caddy is running: systemctl status caddy or curl http://localhost:2019/config/
  2. Omit --address so the CLI discovers the admin address from the config file
  3. Check the admin.listen value in your config and match the CLI flags to it
  4. For unix sockets, ensure the CLI runs as a user with permission on the socket file

Example fix

# before (admin listens on unix socket per config, CLI guesses TCP default)
caddy reload --address localhost:2019

# after (let the CLI read admin.listen from the config)
caddy reload --config /etc/caddy/Caddyfile
Defensive patterns

Strategy: retry

Validate before calling

# Check reachability before issuing the CLI command:
curl -fsS http://localhost:2019/config/ >/dev/null || echo "admin API not reachable"

Try / catch

if err := cmdReload(); err != nil { if strings.Contains(err.Error(), "performing request") { log.Printf("daemon unreachable: %v — is caddy running?", err) } return err }

Prevention

When it happens

Trigger: Running 'caddy reload', 'caddy stop', or similar while the Caddy daemon is not running; admin endpoint listening on a different address than --address specifies; admin config set to listen on a unix socket the CLI user cannot access; firewall blocking localhost port.

Common situations: 'caddy reload' after the service crashed or was stopped; running the CLI as a different user than the daemon when the admin socket has restrictive permissions; custom admin.listen set in config but not passed to the CLI (omit --address so it is read from the config).

Related errors


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