{"record":{"id":"14fc4ea76f7314c7","repo":"juanfont/headscale","slug":"host-not-defined-in-policy","errorCode":null,"errorMessage":"host not defined in policy","messagePattern":"host not defined in policy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":142,"sourceCode":"\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\")\n\tErrProtocolLeadingZero         = errors.New(\"leading 0 not permitted in protocol number\")\n\tErrProtocolOutOfRange          = errors.New(\"protocol number out of range (0-255)\")\n\tErrAutogroupNotSupported       = errors.New(\"autogroup not supported in headscale\")\n\tErrAutogroupInternetSrc        = errors.New(\"autogroup:internet can only be used in ACL destinations\")\n\tErrAutogroupSelfSrc            = errors.New(\"\\\"autogroup:self\\\" not valid on the src side of a rule\")\n\tErrAutogroupNotSupportedACLSrc = errors.New(\"autogroup not supported for ACL sources\")\n\tErrAutogroupNotSupportedACLDst = errors.New(\"autogroup not supported for ACL destinations\")\n\tErrAutogroupDangerAllDst       = errors.New(\"cannot use autogroup:danger-all as a dst\")\n\tErrAutogroupNotSupportedSSHSrc = errors.New(\"autogroup not supported for SSH sources\")\n\tErrAutogroupNotSupportedSSHDst = errors.New(\"autogroup not supported for SSH destinations\")\n\tErrHostNotDefined              = errors.New(\"host not defined in policy\")\n\tErrSSHSourceAliasNotSupported  = errors.New(\"alias not supported for SSH source\")\n\tErrSSHDestAliasNotSupported    = errors.New(\"alias not supported for SSH destination\")\n\tErrUnknownField                = errors.New(\"unknown field\")\n\tErrProtocolNoSpecificPorts     = errors.New(\"protocol does not support specific ports\")\n\tErrTestEmptyAssertions         = errors.New(\"test entry must have at least one of \\\"accept\\\" or \\\"deny\\\"\")\n\tErrTestProtocolNotAllowed      = errors.New(\"test protocol must be tcp, udp, sctp, or empty\")\n\tErrTestDestinationMultiPort    = errors.New(\"test destination port must be a single port\")\n\tErrTestDestinationCIDR         = errors.New(\"test destination must be a single host, not a CIDR range\")\n\tErrAutogroupInternetTestDst    = errors.New(\"autogroup:internet not valid as a test destination\")\n\tErrSSHTestEmptySrc             = errors.New(\"SSH tests entry must have a non-empty src\")\n\tErrSSHTestEmptyDst             = errors.New(\"SSH tests entry must have at least one dst\")\n\tErrSSHTestDstUnknownTag        = errors.New(\"SSH tests dst contains unknown tag\")\n\tErrSSHTestDstDisallowedElement = errors.New(\"SSH tests dst contains disallowed element\")\n)\n\ntype resolved struct {\n\tips netipx.IPSet\n}","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L124-L160","documentation":"Returned by Policy.validate (hscontrol/policy/v2/types.go:2343, 2380 for ACLs; 2582, 2620 for tests) when a host alias is referenced in an ACL src/dst or a test entry but is not defined in the policy's \"hosts\" map. Hosts are the only way to use a symbolic name in rules; an undefined name fails the whole policy.","triggerScenarios":"Policy with \"acls\": [{\"src\": [\"myhost\"], ...}] where \"hosts\" has no \"myhost\" key. Same check applies to host references inside \"tests\" src/dst. Collected alongside all other validation errors and reported together.","commonSituations":"Typos or case mismatches (MyHost vs myhost), deleting a hosts entry while rules still reference it, renaming a host without updating rules. Note a bare name without @, :, or / parses as a Host alias — so a misspelled username like \"userexample\" (missing @) surfaces as an undefined host, which is confusing.","solutions":["Add the alias to hosts: \"hosts\": {\"myhost\": \"100.100.100.100\"}","Check spelling/case of the reference — host names are case-sensitive","If the name was meant to be a user, add the @ (\"user@example.com\"); a bare word is treated as a host"],"exampleFix":"// before\n\"acls\": [{\"action\": \"accept\", \"src\": [\"myhost\"], \"dst\": [\"*:22\"]}]\n// (no hosts section)\n\n// after\n\"hosts\": {\"myhost\": \"100.100.100.100\"},\n\"acls\": [{\"action\": \"accept\", \"src\": [\"myhost\"], \"dst\": [\"*:22\"]}]","handlingStrategy":"validation","validationCode":"// Collect all bare-word aliases and ensure each is a defined host\nfunc validateHostRefs(hosts map[string]string, refs []string) error {\n\tfor _, r := range refs {\n\t\tif !strings.Contains(r, \"@\") && !strings.Contains(r, \":\") && !strings.Contains(r, \"/\") {\n\t\t\tif _, ok := hosts[r]; !ok {\n\t\t\t\treturn fmt.Errorf(\"host %q not defined\", r)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":"func isHostNotDefined(err error) bool {\n\treturn errors.Is(err, policy.ErrHostNotDefined)\n}","tryCatchPattern":"if err := p.Validate(); err != nil {\n\tif errors.Is(err, policy.ErrHostNotDefined) {\n\t\t// validation collects ALL errors; iterate them to fix every host at once\n\t\treturn fmt.Errorf(\"undefined host alias(es): %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Remember the parse heuristic: bare word = host, user needs @, tag/group need prefixes","Keep hosts and their references in the same diff when renaming","Prefer fully-qualified usernames to avoid accidental host parsing"],"tags":["policy","hosts","acl","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}