juanfont/headscale · error
parsing %s prefix from config: %w
Error message
parsing %s prefix from config: %w
What it means
A configured prefix string failed netip.ParsePrefix during loadConfig: parsePrefixConfig reads the raw string for a prefix key and requires strict CIDR notation (address + / + prefix length). The error names which family's prefix (from the family argument, e.g. IPv4/IPv6) failed to parse. The third return (bool) would have flagged out-of-range prefixes; this error is purely syntactic.
Source
Thrown at hscontrol/types/config.go:1053
fmt.Fprintf(&b, "### %-54s ###\n", line)
}
b.WriteString("### ###\n")
b.WriteString("################################################################")
log.Warn().Msg(b.String())
}
func parsePrefixConfig(key string, standardRange netip.Prefix, family string) (*netip.Prefix, bool, error) {
s := viper.GetString(key)
if s == "" {
return nil, false, nil
}
prefix, err := netip.ParsePrefix(s)
if err != nil {
return nil, false, fmt.Errorf("parsing %s prefix from config: %w", family, err)
}
builder := netipx.IPSetBuilder{}
builder.AddPrefix(standardRange)
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
}
View on GitHub (pinned to 565fd254d0)
Solutions
- Write prefixes in full CIDR form: 100.64.0.0/10, fd7a:115c:a1e0::/48
- Verify with a parser: `python3 -c "import ipaddress; ipaddress.ip_network('100.64.0.0/10')"` or ipcalc
- Keep the IPv4 prefix inside the CGNAT/ULA-style ranges headscale expects
Example fix
# before ip_prefixes: - 100.64.1.5 - fd7a:115c:a1e0::/48 # after ip_prefixes: - 100.64.0.0/10 - fd7a:115c:a1e0::/48
Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate every prefix string:
for _, p := range cfg.Prefixes {
if _, err := netip.ParsePrefix(p); err != nil {
return fmt.Errorf("%s is not valid CIDR (want e.g. 100.64.0.0/10)", p)
}
} Type guard
func isValidCIDR(s string) bool {
_, err := netip.ParsePrefix(s)
return err == nil
} Prevention
- Always use full CIDR notation with an explicit prefix length
- Note the code also flags prefixes outside the standard range via the returned bool — keep prefixes in range
- Automate config checks with netip.ParsePrefix-equivalent validation before deploy
When it happens
Trigger: ip_prefixes containing entries like '100.64.1.5' (missing /10), '10.0.0.0/33' (invalid mask), or 'fd7a:115c::/zz'. Each configured prefix is parsed with its own standardRange and family label.
Common situations: Typing a single IP instead of a CIDR; using hostmask notation; pasting an IPv6 prefix with a compressed/typo'd length; config templating leaving a placeholder string.
Related errors
- 0.0.0.0/0 and ::/0 are not allowed
- errOIDCIssuerInvalid
- unmarshalling dns extra records: %w
- failed to parse ApiKey
- failed to parse oauth client secret
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/4c2350f101ab9033.
Report an issue: GitHub.