{"record":{"id":"a35ade2bc1790577","repo":"juanfont/headscale","slug":"w-q-a35ade","errorCode":null,"errorMessage":"%w: %q","messagePattern":"%w: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":603,"sourceCode":"\nfunc (t *Tag) String() string {\n\treturn string(*t)\n}\n\n// MarshalJSON marshals the Tag to JSON.\nfunc (t *Tag) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(string(*t))\n}\n\n// Host is a string that represents a hostname.\ntype Host string\n\nfunc (h *Host) Validate() error {\n\tif isHost(string(*h)) {\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"%w: %q\", ErrInvalidHostname, *h)\n}\n\nfunc (h *Host) UnmarshalJSON(b []byte) error {\n\t*h = Host(strings.Trim(string(b), `\"`))\n\n\terr := h.Validate()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (h *Host) Resolve(p *Policy, _ types.Users, nodes views.Slice[types.NodeView]) (ResolvedAddresses, error) {\n\treturn newResolvedAddresses(h.resolve(p, nil, nodes))\n}\n\nfunc (h *Host) resolve(p *Policy, _ types.Users, _ views.Slice[types.NodeView]) (*netipx.IPSet, error) {","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L585-L621","documentation":"Host.Validate rejected a hostname alias because it does not satisfy the hostname grammar checked by isHost() — typically characters outside the allowed set, a missing label, leading/trailing dots, or a value that is actually another alias form.","triggerScenarios":"Hosts-map or destination values like 'web server' (space), '.web', 'web..example', or 'user@host' assigned to a Host field. isHost(string(h)) returns false in Host.Validate.","commonSituations":"Hand-editing the hosts section with display names containing spaces or underscores; pasting FQDNs with trailing dots; using an email or tag where a hostname was expected.","solutions":["Use a DNS-style name: lowercase letters, digits, hyphens, and dots, each label non-empty, e.g. 'web.example.com'.","Remove spaces, underscores, and stray punctuation.","If the value is meant to be a different alias type, use the right type (user email, tag:, group:)."],"exampleFix":"// before\n\"hosts\": {\"web server\": \"100.64.0.10/32\"}\n\n// after\n\"hosts\": {\"web-server\": \"100.64.0.10/32\"}","handlingStrategy":"validation","validationCode":"var hostRe = regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$`)\nfunc validHostname(s string) bool { return hostRe.MatchString(s) && len(s) <= 253 }","typeGuard":null,"tryCatchPattern":"if err := host.Validate(); err != nil {\n    if errors.Is(err, v2.ErrInvalidHostname) {\n        // strip spaces/underscores; use DNS-style label\n    }\n    return err\n}","preventionTips":["Use DNS-label rules for hosts entries: no spaces or underscores.","Run prettier/json lint plus a hosts-specific check on policy files.","Prefer machine-generated hosts maps over hand-edited ones."],"tags":["policy","acl","validation","hosts","configuration","go"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}