{"record":{"id":"fbdb5a068fccc002","repo":"juanfont/headscale","slug":"building-initial-ip-set-w","errorCode":null,"errorMessage":"building initial IP Set: %w","messagePattern":"building initial IP Set: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/ip.go","lineNumber":129,"sourceCode":"\t}\n\n\t// Fetch all the IP Addresses currently handed out from the Database\n\t// and add them to the used IP set.\n\tfor _, addrStr := range append(v4s, v6s...) {\n\t\tif addrStr.Valid {\n\t\t\taddr, err := netip.ParseAddr(addrStr.String)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parsing IP address from database: %w\", err)\n\t\t\t}\n\n\t\t\tips.Add(addr)\n\t\t}\n\t}\n\n\t// Build the initial IPSet to validate that we can use it.\n\t_, err := ips.IPSet()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"building initial IP Set: %w\",\n\t\t\terr,\n\t\t)\n\t}\n\n\tret.usedIPs = ips\n\n\treturn &ret, nil\n}\n\nfunc (i *IPAllocator) Next() (*netip.Addr, *netip.Addr, error) {\n\tvar (\n\t\terr  error\n\t\tret4 *netip.Addr\n\t\tret6 *netip.Addr\n\t)\n\n\tif i.prefix4 != nil {","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/ip.go#L111-L147","documentation":"After adding all reserved and used addresses to a netipx.IPSetBuilder, the constructor calls IPSet() once to validate. Failure means the accumulated set is internally inconsistent — practically, that configured prefixes contain invalid or contradictory addresses (e.g. a misparsed ip_prefixes entry producing a bad reserved address).","triggerScenarios":"ip_prefixes config containing a value that netipx cannot reconcile into a ranged set — invalid CIDR, mixed-family bytes fed into the builder, or a zero-value prefix producing an invalid endpoint via GetIPPrefixEndpoints.","commonSituations":"Hand-edited ip_prefixes with a typo ('100.64.0.0/10 ' with space, 'fd7a:115c:a1e0::/48' mistyped); config migrated from an old format; environment variable override mistakes.","solutions":["Validate every entry in ip_prefixes is a well-formed CIDR of the intended family.","Reset to the defaults (100.64.0.0/10 and fd7a:115c:a1e0::/48) and confirm startup, then re-apply custom prefixes one by one.","Ensure prefixes do not overlap each other."],"exampleFix":"# before (config.yaml)\nip_prefixes:\n  - 100.64.0.0/10\n  - fd7a:115c:a1e0::49 # typo, not a /48 prefix\n\n# after\nip_prefixes:\n  - 100.64.0.0/10\n  - fd7a:115c:a1e0::/48","handlingStrategy":"validation","validationCode":"import \"net/netip\"\n\nfunc validatePrefixes(raw []string) error {\n    for _, s := range raw {\n        p, err := netip.ParsePrefix(strings.TrimSpace(s))\n        if err != nil {\n            return fmt.Errorf(\"invalid prefix %q: %w\", s, err)\n        }\n        if p != p.Masked() {\n            return fmt.Errorf(\"prefix %q has host bits set\", s)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// Config-level failure with no runtime recovery: fix ip_prefixes and\n// restart. Validate prefixes in a config linter before deploy.","preventionTips":["Lint ip_prefixes as masked CIDRs in CI.","Prefer the default prefixes unless address space forces customization."],"tags":["config","ip-allocation","cidr","netipx"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}