{"record":{"id":"bef05ef99022f25e","repo":"juanfont/headscale","slug":"invalid-alias-format","errorCode":null,"errorMessage":"invalid alias format","messagePattern":"invalid alias format","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":120,"sourceCode":"var 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\")\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\")","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L102-L138","documentation":"parseAlias (hscontrol/policy/v2/types.go:1059) tries to interpret a string as one of the known alias kinds (user, group, tag, autogroup, host, prefix, wildcard). If no branch matches and the fallback regex/format check fails, ErrInvalidAlias with the raw value is returned — the token is not any recognizable alias.","triggerScenarios":"An alias string in src/dst/owners that fits no grammar: mixed prefixes like 'group:tag:web', 'tag:group:x', empty strings, strings with stray spaces or brackets. The value is included in the error for identification.","commonSituations":"Concatenating prefixes by mistake; template rendering injecting empty or whitespace values into alias slots; copy-paste leaving a partial edit like 'group:' with no name.","solutions":["Use exactly one alias prefix per token (group:/tag:/autogroup:) or a bare email/host/IP","Trim whitespace and check for empty strings from templates","Cross-check every alias against the token grammar in headscale's ACL docs"],"exampleFix":"// before\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"group:tag:web\"], \"dst\": [\"*:*\"]}]}\n// after\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"tag:web\"], \"dst\": [\"*:*\"]}]}","handlingStrategy":"validation","validationCode":"var aliasRe = regexp.MustCompile(`^(group:|tag:|autogroup:)?[\\w.@*-]+$`)\nif !aliasRe.MatchString(tok) || strings.Contains(tok, \" \") || tok == \"\" {\n    return fmt.Errorf(\"invalid alias %q\", tok)\n}","typeGuard":"func looksLikeAlias(s string) bool {\n    return s != \"\" && !strings.ContainsAny(s, \" {}[]\") &&\n        strings.Count(s, \"group:\")+strings.Count(s, \"tag:\")+strings.Count(s, \"autogroup:\") <= 1\n}","tryCatchPattern":"if errors.Is(err, policy.ErrInvalidAlias) {\n    // message shows the token; use exactly one prefix or a bare identity\n}","preventionTips":["One prefix per token","Trim template output; reject empty strings","Prefer building aliases from vetted constants"],"tags":["headscale","policy","alias","acl","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}