juanfont/headscale · error

invalid alias format

Error message

invalid alias format

What it means

parseAlias (hscontrol/policy/v2/types.go:1059) tries to interpret a string as one of the known alias kinds (user, group, tag, autogroup, host, prefix, wildcard). If no branch matches and the fallback regex/format check fails, ErrInvalidAlias with the raw value is returned — the token is not any recognizable alias.

Source

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

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")
	ErrAutoApproverNotAlias        = errors.New("auto approver is not an alias")
	ErrInvalidACLAction            = errors.New("invalid ACL action")
	ErrInvalidSSHAction            = errors.New("invalid SSH action")
	ErrInvalidProtocolNumber       = errors.New("invalid protocol number")
	ErrProtocolLeadingZero         = errors.New("leading 0 not permitted in protocol number")
	ErrProtocolOutOfRange          = errors.New("protocol number out of range (0-255)")
	ErrAutogroupNotSupported       = errors.New("autogroup not supported in headscale")
	ErrAutogroupInternetSrc        = errors.New("autogroup:internet can only be used in ACL destinations")
	ErrAutogroupSelfSrc            = errors.New("\"autogroup:self\" not valid on the src side of a rule")
	ErrAutogroupNotSupportedACLSrc = errors.New("autogroup not supported for ACL sources")
	ErrAutogroupNotSupportedACLDst = errors.New("autogroup not supported for ACL destinations")

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Use exactly one alias prefix per token (group:/tag:/autogroup:) or a bare email/host/IP
  2. Trim whitespace and check for empty strings from templates
  3. Cross-check every alias against the token grammar in headscale's ACL docs

Example fix

// before
{"acls": [{"action": "accept", "src": ["group:tag:web"], "dst": ["*:*"]}]}
// after
{"acls": [{"action": "accept", "src": ["tag:web"], "dst": ["*:*"]}]}
Defensive patterns

Strategy: validation

Validate before calling

var aliasRe = regexp.MustCompile(`^(group:|tag:|autogroup:)?[\w.@*-]+$`)
if !aliasRe.MatchString(tok) || strings.Contains(tok, " ") || tok == "" {
    return fmt.Errorf("invalid alias %q", tok)
}

Type guard

func looksLikeAlias(s string) bool {
    return s != "" && !strings.ContainsAny(s, " {}[]") &&
        strings.Count(s, "group:")+strings.Count(s, "tag:")+strings.Count(s, "autogroup:") <= 1
}

Try / catch

if errors.Is(err, policy.ErrInvalidAlias) {
    // message shows the token; use exactly one prefix or a bare identity
}

Prevention

When it happens

Trigger: An alias string in src/dst/owners that fits no grammar: mixed prefixes like 'group:tag:web', 'tag:group:x', empty strings, strings with stray spaces or brackets. The value is included in the error for identification.

Common situations: Concatenating prefixes by mistake; template rendering injecting empty or whitespace values into alias slots; copy-paste leaving a partial edit like 'group:' with no name.

Related errors


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