{"record":{"id":"6688ebc12f4924d2","repo":"juanfont/headscale","slug":"nodeattrs-target-does-not-support-this-autogroup","errorCode":null,"errorMessage":"nodeAttrs target does not support this autogroup","messagePattern":"nodeAttrs target does not support this autogroup","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":90,"sourceCode":")\n\n// Grant validation errors.\nvar (\n\tErrGrantMissingIPOrApp             = errors.New(\"ip and app can not both be empty\")\n\tErrGrantViaNotATag                 = errors.New(\"via can only be a tag\")\n\tErrProtocolPortInvalidFormat       = errors.New(\"expected only one colon in Internet protocol and port type\")\n\tErrCapNameInvalidForm              = errors.New(\"capability name must have the form {domain}/{path}\")\n\tErrCapNameTailscaleDomain          = errors.New(\"capability name must not be in the tailscale.com domain\")\n\tErrGrantAutogroupSelfInvalidSource = errors.New(\"autogroup:self can only be used with users, groups, or supported autogroups\")\n\tErrGrantAppWithAutogroupInternet   = errors.New(\"cannot use app grants with autogroup:internet\")\n\tErrGrantDefaultRouteCIDR           = errors.New(\"to allow all IP addresses, use \\\"*\\\" or \\\"autogroup:internet\\\"\")\n)\n\n// NodeAttrs validation errors.\nvar (\n\tErrNodeAttrsIPPoolReserved      = errors.New(\"nodeAttrs ipPool must not overlap reserved Tailscale ranges\")\n\tErrNodeAttrsIPPoolOutOfRange    = errors.New(\"nodeAttrs ipPool must be within 100.64.0.0/10\")\n\tErrNodeAttrsAutogroupNotAllowed = errors.New(\"nodeAttrs target does not support this autogroup\")\n\tErrNodeAttrUnsupported          = errors.New(\"nodeAttrs uses a feature headscale does not yet support\")\n\tErrNodeAttrIPPoolUnsupported    = errors.New(\"nodeAttrs ipPool requires the IP allocator (https://github.com/juanfont/headscale/issues/2912)\")\n\tErrNodeAttrTargetUnsupported    = errors.New(\"nodeAttrs target alias type is not supported\")\n)\n\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 @\")","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L72-L108","documentation":"Thrown by validateAutogroupForNodeAttrs (hscontrol/policy/v2/types.go:2082) when a nodeAttrs block targets an autogroup that cannot identify a stable set of nodes. Only autogroup:member and autogroup:tagged are valid nodeAttrs targets; autogroup:self, autogroup:internet, and autogroup:danger-all describe per-request or non-identity sets, so a node-level attribute cannot attach to them. The error message includes the offending value and the allowed list.","triggerScenarios":"A policy (HuJSON/JSON ACL file or db-stored policy) containing e.g. {\"nodeAttrs\": [{\"target\": [\"autogroup:self\"], \"attrs\": [...]}]} fails at policy load/compile. Also triggered by autogroup:internet or autogroup:danger-all as a nodeAttrs target.","commonSituations":"Copying a Tailscale SaaS policy that uses autogroup:self or autogroup:internet in nodeAttrs; assuming any autogroup works anywhere an alias is accepted; upgrading from a policy format that tolerated these targets.","solutions":["Change the nodeAttrs target to autogroup:member or autogroup:tagged","If the attribute is per-user, attach it to explicit usernames or groups instead of an autogroup","If you truly need self-scoped behaviour, express it via grants with autogroup:self rather than nodeAttrs"],"exampleFix":"// before\n{\"nodeAttrs\": [{\"target\": [\"autogroup:self\"], \"app\": {\"tailcfg-app-foo\": [\"*\"]}}]}\n// after\n{\"nodeAttrs\": [{\"target\": [\"autogroup:member\"], \"app\": {\"tailcfg-app-foo\": [\"*\"]}}]}","handlingStrategy":"validation","validationCode":"// before compiling, check nodeAttrs targets\nallowed := map[string]bool{\"autogroup:member\": true, \"autogroup:tagged\": true}\nfor _, na := range policy.NodeAttrs {\n    for _, t := range na.Target {\n        if strings.HasPrefix(t, \"autogroup:\") && !allowed[t] {\n            return fmt.Errorf(\"nodeAttrs target %q not allowed\", t)\n        }\n    }\n}","typeGuard":"func isValidNodeAttrsAutogroup(s string) bool {\n    return s == \"autogroup:member\" || s == \"autogroup:tagged\"\n}","tryCatchPattern":"err := policy.Compile(...)\nif errors.Is(err, policy.ErrNodeAttrsAutogroupNotAllowed) {\n    // report allowed targets from the message and fix the policy\n}","preventionTips":["Only use autogroup:member/autogroup:tagged as nodeAttrs targets","Run 'headscale policy check' in CI on every policy change","Keep a lint step that whitelists autogroups per position"],"tags":["headscale","policy","acl","autogroup","nodeattrs","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}