juanfont/headscale · error

user not found

Error message

user not found

What it means

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.

Source

Thrown at hscontrol/policy/v2/types.go:109

	ErrNodeAttrUnsupported          = errors.New("nodeAttrs uses a feature headscale does not yet support")
	ErrNodeAttrIPPoolUnsupported    = errors.New("nodeAttrs ipPool requires the IP allocator (https://github.com/juanfont/headscale/issues/2912)")
	ErrNodeAttrTargetUnsupported    = errors.New("nodeAttrs target alias type is not supported")
)

// nodeAttrUnsupportedCaps lists caps that headscale parses but cannot act on
// today. Each entry maps to the tracking issue an operator can follow. The
// caps are accepted by Tailscale SaaS, but delivering them via headscale
// without the matching server-side machinery would be misleading — nodes
// would advertise a feature that does not work. Reject at policy load and
// point operators at the issue.
var nodeAttrUnsupportedCaps = map[tailcfg.NodeCapability]string{
	tailcfg.NodeAttrFunnel: "https://github.com/juanfont/headscale/issues/2527",
}

// Policy validation errors.
var (
	ErrInvalidUsername             = errors.New("username must contain @")
	ErrUserNotFound                = errors.New("user not found")
	ErrMultipleUsersFound          = errors.New("multiple users found")
	ErrInvalidGroupFormat          = errors.New("group must start with 'group:'")
	ErrInvalidTagFormat            = errors.New("tag must start with 'tag:'")
	ErrInvalidHostname             = errors.New("invalid hostname")
	ErrHostResolve                 = errors.New("error resolving host")
	ErrInvalidPrefix               = errors.New("invalid prefix")
	ErrInvalidAutogroup            = errors.New("invalid autogroup")
	ErrUnknownAutogroup            = errors.New("unknown autogroup")
	ErrHostportMissingColon        = errors.New("hostport must contain a colon")
	ErrTypeNotSupported            = errors.New("type not supported")
	ErrInvalidAlias                = errors.New("invalid alias format")
	ErrInvalidAutoApprover         = errors.New("invalid auto approver format")
	ErrInvalidOwner                = errors.New("invalid owner format")
	ErrGroupNotDefined             = errors.New("group not defined in policy")
	ErrInvalidGroupMember          = errors.New("invalid group member type")
	ErrGroupValueNotArray          = errors.New("group value must be an array of users")
	ErrInvalidHostIP               = errors.New("hostname contains invalid IP address")
	ErrTagNotDefined               = errors.New("tag not found")

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Run 'headscale users list' and verify the exact Email/Name
  2. Fix the policy token to match a real user (email, or name with trailing '@')
  3. If the user was deleted, recreate it or remove the reference from groups/tagOwners/grants

Example fix

// before
{"groups": {"group:admins": ["alices@example.com"]}}
// after (actual user email is alice@example.com)
{"groups": {"group:admins": ["alice@example.com"]}}
Defensive patterns

Strategy: validation

Validate before calling

// verify the token matches a known user before compiling
func userExists(users []types.User, tok string) bool {
    tok = strings.TrimSuffix(tok, "@")
    for _, u := range users {
        if (u.ProviderIdentifier.Valid && u.ProviderIdentifier.String == tok) ||
            u.Email == tok || u.Name == tok {
            return true
        }
    }
    return false
}

Type guard

null

Try / catch

if errors.Is(err, policy.ErrUserNotFound) {
    // list users, fix or drop the reference, recompile
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/2495a6fc1ec31ee3. Report an issue: GitHub.