{"record":{"id":"75529b1c75633af8","repo":"juanfont/headscale","slug":"invalid-hostname","errorCode":null,"errorMessage":"invalid hostname","messagePattern":"invalid hostname","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":113,"sourceCode":"\n// nodeAttrUnsupportedCaps lists caps that headscale parses but cannot act on\n// today. Each entry maps to the tracking issue an operator can follow. The\n// caps are accepted by Tailscale SaaS, but delivering them via headscale\n// without the matching server-side machinery would be misleading — nodes\n// would advertise a feature that does not work. Reject at policy load and\n// point operators at the issue.\nvar nodeAttrUnsupportedCaps = map[tailcfg.NodeCapability]string{\n\ttailcfg.NodeAttrFunnel: \"https://github.com/juanfont/headscale/issues/2527\",\n}\n\n// Policy validation errors.\nvar (\n\tErrInvalidUsername             = errors.New(\"username must contain @\")\n\tErrUserNotFound                = errors.New(\"user not found\")\n\tErrMultipleUsersFound          = errors.New(\"multiple users found\")\n\tErrInvalidGroupFormat          = errors.New(\"group must start with 'group:'\")\n\tErrInvalidTagFormat            = errors.New(\"tag must start with 'tag:'\")\n\tErrInvalidHostname             = errors.New(\"invalid hostname\")\n\tErrHostResolve                 = errors.New(\"error resolving host\")\n\tErrInvalidPrefix               = errors.New(\"invalid prefix\")\n\tErrInvalidAutogroup            = errors.New(\"invalid autogroup\")\n\tErrUnknownAutogroup            = errors.New(\"unknown autogroup\")\n\tErrHostportMissingColon        = errors.New(\"hostport must contain a colon\")\n\tErrTypeNotSupported            = errors.New(\"type not supported\")\n\tErrInvalidAlias                = errors.New(\"invalid alias format\")\n\tErrInvalidAutoApprover         = errors.New(\"invalid auto approver format\")\n\tErrInvalidOwner                = errors.New(\"invalid owner format\")\n\tErrGroupNotDefined             = errors.New(\"group not defined in policy\")\n\tErrInvalidGroupMember          = errors.New(\"invalid group member type\")\n\tErrGroupValueNotArray          = errors.New(\"group value must be an array of users\")\n\tErrInvalidHostIP               = errors.New(\"hostname contains invalid IP address\")\n\tErrTagNotDefined               = errors.New(\"tag not found\")\n\tErrAutoApproverNotAlias        = errors.New(\"auto approver is not an alias\")\n\tErrInvalidACLAction            = errors.New(\"invalid ACL action\")\n\tErrInvalidSSHAction            = errors.New(\"invalid SSH action\")\n\tErrInvalidProtocolNumber       = errors.New(\"invalid protocol number\")","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L95-L131","documentation":"Host.Validate (hscontrol/policy/v2/types.go:598-603) runs the token through isHost and rejects anything that is not a syntactically valid hostname. Host aliases in ACLs must be valid DNS names; they are later resolved against the policy's hosts map (not DNS).","triggerScenarios":"An alias string that is neither a prefix (IP/CIDR), nor user/group/tag/autogroup token, parses as Host and then fails isHost — e.g. underscores, spaces, stray punctuation, '*web', or a typo like 'exam ple.example.com'. Fails at policy UnmarshalJSON.","commonSituations":"Typos in hostnames inside ACL dst lists; using underscores (invalid in DNS hostnames per this validator); forgetting that wildcards are only allowed as the standalone '*' token.","solutions":["Fix the hostname to a valid DNS name (letters, digits, hyphens, dots)","For IP targets use CIDR prefixes instead of hostnames","Define the name under the hosts map so resolution also succeeds"],"exampleFix":"// before\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"group:admins\"], \"dst\": [\"my_server:80\"]}]}\n// after\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"group:admins\"], \"dst\": [\"my-server:80\"]}]}","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])?)*$`)\nif !hostRe.MatchString(host) { return fmt.Errorf(\"invalid hostname %q\", host) }","typeGuard":"func isValidHostname(s string) bool { return hostRe.MatchString(s) }","tryCatchPattern":"if errors.Is(err, policy.ErrInvalidHostname) {\n    // fix the name: no underscores/spaces/wildcards mid-name\n}","preventionTips":["Use hyphens not underscores in hostnames","Use CIDRs for IP targets","Run policy check before applying"],"tags":["headscale","policy","hostname","acl","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}