{"record":{"id":"3fdac6b29b76b483","repo":"juanfont/headscale","slug":"invalid-auto-approver-format","errorCode":null,"errorMessage":"invalid auto approver format","messagePattern":"invalid auto approver format","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":121,"sourceCode":"\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\")\n\tErrAutogroupDangerAllDst       = errors.New(\"cannot use autogroup:danger-all as a dst\")","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L103-L139","documentation":"AutoApprovers parsing (hscontrol/policy/v2/types.go:1205) validates each auto-approver token in the autoApprovers section (routes/exitNode lists). A token that is not a valid approver alias — not a user, group, tag, or supported autogroup in the expected form — is rejected with this sentinel including the string.","triggerScenarios":"{\"autoApprovers\": {\"routes\": {\"10.0.0.0/8\": [\"bad token\"]}}} where the entry has a wrong prefix, empty value, or an approver form the parser rejects (e.g. a hostname or CIDR, which cannot approve routes).","commonSituations":"Putting an IP or hostname in the approver list instead of an identity; typo'd group:/tag: prefix; assuming any dst-style alias is valid as an approver.","solutions":["Use identities as approvers: group:..., tag:..., user email, or a supported autogroup","Verify each autoApprovers entry resolves like a src-side alias","Check the parsed policy with 'headscale policy check'"],"exampleFix":"// before\n{\"autoApprovers\": {\"routes\": {\"10.0.0.0/8\": [\"10.0.0.1\"]}}}\n// after\n{\"autoApprovers\": {\"routes\": {\"10.0.0.0/8\": [\"group:admins\"]}}}","handlingStrategy":"validation","validationCode":"func isValidApprover(s string) bool {\n    return strings.HasPrefix(s, \"group:\") || strings.HasPrefix(s, \"tag:\") ||\n        strings.Contains(s, \"@\") || strings.HasPrefix(s, \"autogroup:\")\n}\nfor _, apps := range policy.AutoApprovers.Routes {\n    for _, a := range apps { if !isValidApprover(a) { return fmt.Errorf(\"bad approver %q\", a) } }\n}","typeGuard":"func isApproverToken(s string) bool { return isValidApprover(s) }","tryCatchPattern":"if errors.Is(err, policy.ErrInvalidAutoApprover) {\n    // replace non-identity tokens with users/groups\n}","preventionTips":["Approvers are identities, never IPs/hostnames","Reuse src-side alias validation for approver lists"],"tags":["headscale","policy","autoapprovers","routes","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}