{"record":{"id":"2495a6fc1ec31ee3","repo":"juanfont/headscale","slug":"user-not-found-2495a6","errorCode":null,"errorMessage":"user not found","messagePattern":"user not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":109,"sourceCode":"\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 @\")\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\")","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L91-L127","documentation":"resolveUser (hscontrol/policy/v2/types.go:388-409) searches the users known to headscale by ProviderIdentifier first, then Email, then Name (with the trailing '@' stripped). If none match, this sentinel is returned wrapped with the token, and policy compilation fails — references to nonexistent users are not silently ignored.","triggerScenarios":"A policy referencing \"user@example.com\" or \"name@\" when no headscale user has that Email, Name, or ProviderIdentifier. Surfaced during Resolve at policy compile (e.g. from compileNodeAttrs or grant resolution) and via headscale policy check / reload.","commonSituations":"User was deleted or renamed after the policy was written; OIDC users referenced by an email that differs from their ProviderIdentifier; typo in the username; policy written before users were provisioned.","solutions":["Run 'headscale users list' and verify the exact Email/Name","Fix the policy token to match a real user (email, or name with trailing '@')","If the user was deleted, recreate it or remove the reference from groups/tagOwners/grants"],"exampleFix":"// before\n{\"groups\": {\"group:admins\": [\"alices@example.com\"]}}\n// after (actual user email is alice@example.com)\n{\"groups\": {\"group:admins\": [\"alice@example.com\"]}}","handlingStrategy":"validation","validationCode":"// verify the token matches a known user before compiling\nfunc userExists(users []types.User, tok string) bool {\n    tok = strings.TrimSuffix(tok, \"@\")\n    for _, u := range users {\n        if (u.ProviderIdentifier.Valid && u.ProviderIdentifier.String == tok) ||\n            u.Email == tok || u.Name == tok {\n            return true\n        }\n    }\n    return false\n}","typeGuard":"null","tryCatchPattern":"if errors.Is(err, policy.ErrUserNotFound) {\n    // list users, fix or drop the reference, recompile\n}","preventionTips":["Sync policy references after deleting/renaming users","Prefer groups over direct usernames in rules","Run 'headscale policy check' after user lifecycle changes"],"tags":["headscale","policy","users","oidc","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}