{"record":{"id":"b8a6b2c6493ce740","repo":"juanfont/headscale","slug":"requested-tags","errorCode":null,"errorMessage":"requested tags","messagePattern":"requested tags","errorType":"http","errorClass":"ErrRequestedTagsInvalidOrNotPermitted","httpStatus":400,"severity":"error","filePath":"hscontrol/state/tags.go","lineNumber":20,"sourceCode":"\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/juanfont/headscale/hscontrol/types\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nvar (\n\t// ErrNodeMarkedTaggedButHasNoTags is returned when a node is marked as tagged but has no tags.\n\tErrNodeMarkedTaggedButHasNoTags = errors.New(\"node marked as tagged but has no tags\")\n\n\t// ErrNodeHasNeitherUserNorTags is returned when a node has neither a user nor tags.\n\tErrNodeHasNeitherUserNorTags = errors.New(\"node has neither user nor tags - must be owned by user or tagged\")\n\n\t// ErrRequestedTagsInvalidOrNotPermitted is returned when requested tags are invalid or not permitted.\n\t// This message format matches Tailscale SaaS: \"requested tags [tag:xxx] are invalid or not permitted\".\n\tErrRequestedTagsInvalidOrNotPermitted = errors.New(\"requested tags\")\n)\n\n// ErrTaggedNodeHasUser is returned when a tagged node has a [types.Node.UserID] set.\nvar ErrTaggedNodeHasUser = errors.New(\"tagged node must not have user_id set\")\n\n// validateNodeOwnership ensures proper node ownership model.\n// A node must be either user-owned or tagged, and these are mutually exclusive:\n// tagged nodes must not have a [types.Node.UserID], and user-owned nodes must\n// not have tags.\nfunc validateNodeOwnership(node *types.Node) error {\n\tif node.IsTagged() {\n\t\tif len(node.Tags) == 0 {\n\t\t\treturn fmt.Errorf(\"%w: %q\", ErrNodeMarkedTaggedButHasNoTags, node.Hostname)\n\t\t}\n\n\t\tif node.UserID != nil {\n\t\t\treturn fmt.Errorf(\"%w: %q\", ErrTaggedNodeHasUser, node.Hostname)\n\t\t}","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/state/tags.go#L2-L38","documentation":"ErrRequestedTagsInvalidOrNotPermitted is a sentinel in hscontrol/state/tags.go:20 whose short message is deliberately wrapped with detail to match the Tailscale SaaS format \"requested tags [tag:xxx] are invalid or not permitted\". It is returned when a node requests tags that are malformed or whose owning policy does not permit the requesting node (tag owners rule in policy v2).","triggerScenarios":"A node registers with Hostinfo request tags like tag:prod but the policy's tagOwners section does not grant that tag to the node's user/IPs; requesting a syntactically invalid tag; changing a pre-auth key or user so previously-granted tags are no longer owned.","commonSituations":"Policy file missing a tagOwners entry for a tag nodes advertise; adding request tags in tailscale_up ('--advertise-tags=tag:x') without updating the ACL policy first; typos in tag names between node config and policy.","solutions":["Add the tag to policy with a tagOwners entry granting it to the requesting node's owner (e.g. the user or a group)","Verify the exact tag string matches between the node's --advertise-tags and the policy","Reload the policy after editing (policy mode file: fix and save; db: update via API)","If the tag is not intended, remove it from the node's advertised tags"],"exampleFix":"// before: policy has no owner for tag:prod, node advertises tag:prod\n\"tagOwners\": { \"tag:server\": [\"group:admin\"] }\n\n// after\n\"tagOwners\": { \"tag:server\": [\"group:admin\"], \"tag:prod\": [\"group:admin\"] }","handlingStrategy":"validation","validationCode":"// Before advertising a tag, confirm the policy grants it to the node's owner\nowners := policy.TagOwners()[\"tag:prod\"]\nif !owners.Contains(nodeOwner) { return errors.New(\"tag not permitted\") }","typeGuard":"null","tryCatchPattern":"if errors.Is(err, state.ErrRequestedTagsInvalidOrNotPermitted) { /* fix tagOwners in policy, reload, re-register */ }","preventionTips":["Add tagOwners entries before rolling out --advertise-tags to nodes","Keep tag names consistent between node config and policy; reload policy after edits"],"tags":["headscale","tags","policy","acl","registration"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}