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
- Correct the offending entry to a valid CIDR prefix, e.g. 10.0.0.1/32 or 172.16.0.0/12
- Make sure every list element is quoted consistently in YAML/JSON and has no trailing spaces
- 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
- Treat trusted_proxies entries as CIDRs, not bare IPs — always include /NN
- Run `headscale config validate` (or equivalent load in dry-run) in CI for config files
- Use YAML anchors/schema checks to catch unquoted or malformed list entries
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
- creating OIDC provider from issuer config: %w
- errTrustedProxyZeroRange
- STUN address not set
- initial DERPMap is empty, Headscale requires at least one en
- database type not supported
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/a922b403391bd49e.
Report an issue: GitHub.