juanfont/headscale · error

trusted_proxies[%d] %q: %w

Error message

trusted_proxies[%d] %q: %w

What it means

Thrown while parsing the trusted_proxies configuration list. Each entry must be a valid CIDR prefix parseable by netip.ParsePrefix; when entry i fails to parse, its index, raw string, and the underlying parse error are wrapped and returned. This aborts server startup so that a typo in the proxy list cannot silently disable X-Forwarded-For handling.

Source

Thrown at hscontrol/types/config.go:1076

	ipSet, _ := builder.IPSet()

	return &prefix, !ipSet.ContainsPrefix(prefix), nil
}

// trustedProxies rejects 0.0.0.0/0 and ::/0 because they defeat the
// peer-trust gate and almost always indicate misconfiguration.
func trustedProxies() ([]netip.Prefix, error) {
	raw := viper.GetStringSlice("trusted_proxies")
	if len(raw) == 0 {
		return nil, nil
	}

	out := make([]netip.Prefix, 0, len(raw))
	for i, s := range raw {
		p, err := netip.ParsePrefix(s)
		if err != nil {
			return nil, fmt.Errorf("trusted_proxies[%d] %q: %w", i, s, err)
		}

		if p.Bits() == 0 {
			return nil, fmt.Errorf("trusted_proxies[%d] %q: %w", i, s, errTrustedProxyZeroRange)
		}

		out = append(out, p.Masked())
	}

	return out, nil
}

// LoadCLIConfig returns the needed configuration for the CLI client
// of Headscale to connect to a Headscale server.
func LoadCLIConfig() (*Config, error) {
	logConfig := logConfig()
	zerolog.SetGlobalLevel(logConfig.Level)

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Correct the offending entry to a valid CIDR prefix, e.g. 10.0.0.1/32 or 172.16.0.0/12
  2. Make sure every list element is quoted consistently in YAML/JSON and has no trailing spaces
  3. Leave trusted_proxies unset if you do not sit behind a proxy — the empty list returns nil, nil

Example fix

# before
trusted_proxies:
  - 10.0.0.1
# after
trusted_proxies:
  - 10.0.0.1/32
Defensive patterns

Strategy: validation

Validate before calling

// validate before config load / server start
import "net/netip"

func validateTrustedProxies(raw []string) error {
	for i, s := range raw {
		if _, err := netip.ParsePrefix(s); err != nil {
			return fmt.Errorf("entry %d (%q) is not a valid CIDR: %w", i, s, err)
		}
	}
	return nil
}

Try / catch

err := hskinValidate(cfg) // config load
if err != nil {
    log.Fatal().Err(err).Msg("invalid configuration")
}

Prevention

When it happens

Trigger: Setting trusted_proxies in config (e.g. trusted_proxies: ["10.0.0.1/33", "not-a-cidr", "10.0.0.x"]) and calling the config load path that invokes trustedProxies(). Any string that netip.ParsePrefix rejects (bad bits value, bare IP without /mask, garbage) triggers it.

Common situations: Copy-pasting a reverse-proxy IP without the CIDR suffix (e.g. "10.0.0.1" instead of "10.0.0.1/32"), typos, or leftover YAML quoting artifacts. Common after adding an nginx/traefik load balancer in front of headscale.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/a922b403391bd49e. Report an issue: GitHub.