{"record":{"id":"4c2350f101ab9033","repo":"juanfont/headscale","slug":"parsing-s-prefix-from-config-w","errorCode":null,"errorMessage":"parsing %s prefix from config: %w","messagePattern":"parsing (.+?) prefix from config: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/types/config.go","lineNumber":1053,"sourceCode":"\t\tfmt.Fprintf(&b, \"###  %-54s  ###\\n\", line)\n\t}\n\n\tb.WriteString(\"###                                                          ###\\n\")\n\tb.WriteString(\"################################################################\")\n\n\tlog.Warn().Msg(b.String())\n}\n\nfunc parsePrefixConfig(key string, standardRange netip.Prefix, family string) (*netip.Prefix, bool, error) {\n\ts := viper.GetString(key)\n\n\tif s == \"\" {\n\t\treturn nil, false, nil\n\t}\n\n\tprefix, err := netip.ParsePrefix(s)\n\tif err != nil {\n\t\treturn nil, false, fmt.Errorf(\"parsing %s prefix from config: %w\", family, err)\n\t}\n\n\tbuilder := netipx.IPSetBuilder{}\n\tbuilder.AddPrefix(standardRange)\n\n\tipSet, _ := builder.IPSet()\n\n\treturn &prefix, !ipSet.ContainsPrefix(prefix), nil\n}\n\n// trustedProxies rejects 0.0.0.0/0 and ::/0 because they defeat the\n// peer-trust gate and almost always indicate misconfiguration.\nfunc trustedProxies() ([]netip.Prefix, error) {\n\traw := viper.GetStringSlice(\"trusted_proxies\")\n\tif len(raw) == 0 {\n\t\treturn nil, nil\n\t}\n","sourceCodeStart":1035,"sourceCodeEnd":1071,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/types/config.go#L1035-L1071","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nip_prefixes:\n  - 100.64.1.5\n  - fd7a:115c:a1e0::/48\n\n# after\nip_prefixes:\n  - 100.64.0.0/10\n  - fd7a:115c:a1e0::/48","handlingStrategy":"validation","validationCode":"// Pre-validate every prefix string:\nfor _, p := range cfg.Prefixes {\n    if _, err := netip.ParsePrefix(p); err != nil {\n        return fmt.Errorf(\"%s is not valid CIDR (want e.g. 100.64.0.0/10)\", p)\n    }\n}","typeGuard":"func isValidCIDR(s string) bool {\n    _, err := netip.ParsePrefix(s)\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["config","networking","cidr","validation","go"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}