{"record":{"id":"9f7f8bfe338b25c6","repo":"juanfont/headscale","slug":"w-s-9f7f8b","errorCode":null,"errorMessage":"%w: %s","messagePattern":"%w: %s","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":652,"sourceCode":"\t\terrs = append(errs, err)\n\t}\n\n\t// Address-based aliases (host names) resolve to exactly the\n\t// literal prefix from the hosts map. They do NOT expand to\n\t// include the matching node's other IP addresses.\n\tips.AddPrefix(netip.Prefix(pref))\n\n\treturn buildIPSetMultiErr(&ips, errs)\n}\n\ntype Prefix netip.Prefix\n\nfunc (p *Prefix) Validate() error {\n\tif netip.Prefix(*p).IsValid() {\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"%w: %s\", ErrInvalidPrefix, p.String())\n}\n\nfunc (p *Prefix) String() string {\n\treturn netip.Prefix(*p).String()\n}\n\nfunc (p *Prefix) parseString(addr string) error {\n\tif !strings.Contains(addr, \"/\") {\n\t\taddr, err := netip.ParseAddr(addr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\taddrPref, err := addr.Prefix(addr.BitLen())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n","sourceCodeStart":634,"sourceCodeEnd":670,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L634-L670","documentation":"Prefix.Validate rejected an IP prefix because netip.Prefix(*p).IsValid() is false — the zero Prefix (uninitialized) or an invalid bit combination. The value is printed via %s and typically shows as 'invalid IP' or the zero prefix.","triggerScenarios":"A policy field expects a prefix but receives garbage that still unmarshalled (e.g. via parseString partial failure), or code constructs Prefix{} without initializing it before Validate. netip.Prefix.IsValid() is false.","commonSituations":"Programmatic policy generation leaving zero-value prefixes; JSON with an empty string that bypassed parse errors; copying prefixes between structs and dropping initialization.","solutions":["Ensure the value is a valid CIDR like '100.64.0.0/24' or single IP ('100.64.0.1' is normalized by parseString).","Initialize all Prefix fields before calling Validate; never pass netip.Prefix{}.","Validate at parse time with parseString so bad input fails early with the original parse error."],"exampleFix":"// before\np := policy.Prefix{}\nerr := p.Validate()\n\n// after\nvar p policy.Prefix\nerr := p.parseString(\"100.64.0.0/24\")\nif err == nil { err = p.Validate() }","handlingStrategy":"validation","validationCode":"// Parse-and-validate in one step; never validate zero values.\nvar p v2.Prefix\nif err := p.ParseString(cidrString); err != nil { return err }\nif err := p.Validate(); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := p.Validate(); err != nil {\n    if errors.Is(err, v2.ErrInvalidPrefix) {\n        // re-parse from the original string; zero-value prefix means upstream init bug\n    }\n    return err\n}","preventionTips":["Never construct Prefix zero values; always parse from strings.","Fail at parse time (parseString) so the original netip error is preserved.","Unit-test policy generators to assert every emitted prefix validates."],"tags":["policy","acl","validation","ip-prefix","go"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}