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

  1. Write prefixes in full CIDR form: 100.64.0.0/10, fd7a:115c:a1e0::/48
  2. Verify with a parser: `python3 -c "import ipaddress; ipaddress.ip_network('100.64.0.0/10')"` or ipcalc
  3. 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

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


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