juanfont/headscale · error

invalid hostname

Error message

invalid hostname

What it means

Host.Validate (hscontrol/policy/v2/types.go:598-603) runs the token through isHost and rejects anything that is not a syntactically valid hostname. Host aliases in ACLs must be valid DNS names; they are later resolved against the policy's hosts map (not DNS).

Source

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

// 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")
	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")

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Fix the hostname to a valid DNS name (letters, digits, hyphens, dots)
  2. For IP targets use CIDR prefixes instead of hostnames
  3. Define the name under the hosts map so resolution also succeeds

Example fix

// before
{"acls": [{"action": "accept", "src": ["group:admins"], "dst": ["my_server:80"]}]}
// after
{"acls": [{"action": "accept", "src": ["group:admins"], "dst": ["my-server:80"]}]}
Defensive patterns

Strategy: validation

Validate before calling

var hostRe = regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$`)
if !hostRe.MatchString(host) { return fmt.Errorf("invalid hostname %q", host) }

Type guard

func isValidHostname(s string) bool { return hostRe.MatchString(s) }

Try / catch

if errors.Is(err, policy.ErrInvalidHostname) {
    // fix the name: no underscores/spaces/wildcards mid-name
}

Prevention

When it happens

Trigger: An alias string that is neither a prefix (IP/CIDR), nor user/group/tag/autogroup token, parses as Host and then fails isHost — e.g. underscores, spaces, stray punctuation, '*web', or a typo like 'exam ple.example.com'. Fails at policy UnmarshalJSON.

Common situations: Typos in hostnames inside ACL dst lists; using underscores (invalid in DNS hostnames per this validator); forgetting that wildcards are only allowed as the standalone '*' token.

Related errors


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