{"record":{"id":"e6e88c07888c4e92","repo":"juanfont/headscale","slug":"multiple-users-found","errorCode":null,"errorMessage":"multiple users found","messagePattern":"multiple users found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":110,"sourceCode":"\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\")\n\tErrAutoApproverNotAlias        = errors.New(\"auto approver is not an alias\")","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L92-L128","documentation":"resolveUser (hscontrol/policy/v2/types.go:412-413) collects every user whose Email or Name equals the token (after stripping '@'); if more than one matches, the reference is ambiguous and policy compilation fails with this sentinel including the token and the matched users. hscontrol/policy/v2/policy.go:118 special-cases it during compile to surface it clearly.","triggerScenarios":"Two headscale users share the same Name or Email (e.g. a locally created 'alice' and an OIDC 'alice' with equal Name), and the policy references that bare name/email. ProviderIdentifier matches return early, so this only fires on Email/Name collisions.","commonSituations":"Migrating from local auth to OIDC leaves a duplicate user with the same email; renaming users created duplicates; multiple IdP accounts mapped to the same Name field.","solutions":["Disambiguate by referencing the user's ProviderIdentifier (OIDC sub) instead of email/name","Delete or rename the duplicate user via 'headscale users' commands so only one matches","Put the ambiguous user in a group and reference group:name instead"],"exampleFix":"// before\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"alice@example.com\"], \"dst\": [\"*\"]}]}\n// after (unique OIDC subject)\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"294872317294\"], \"dst\": [\"*\"]}]}","handlingStrategy":"validation","validationCode":"// detect ambiguous Email/Name duplicates before applying policy\ntok := strings.TrimSuffix(token, \"@\")\nmatches := 0\nfor _, u := range users {\n    if u.Email == tok || u.Name == tok { matches++ }\n}\nif matches > 1 { return fmt.Errorf(\"ambiguous user %q\", token) }","typeGuard":"null","tryCatchPattern":"if errors.Is(err, policy.ErrMultipleUsersFound) {\n    // the message lists matched users; switch to ProviderIdentifier or dedupe\n}","preventionTips":["Keep user Emails and Names unique across local and OIDC users","Reference OIDC users by ProviderIdentifier when possible","Audit for duplicate users after auth-provider migrations"],"tags":["headscale","policy","users","ambiguity","oidc"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}