juanfont/headscale · error

%q is not a valid DNS label: %w

Error message

%q is not a valid DNS label: %w

What it means

Returned by types.ValidateGivenName when the candidate name fails dnsname.ValidLabel — i.e. it is not a legal DNS label (empty, >63 chars, illegal characters, leading/trailing hyphen). Admin write paths such as node rename call this first because the mapper cannot build a map for a node whose FQDN cannot be formed.

Source

Thrown at hscontrol/types/node.go:529

		return "", fmt.Errorf(
			"creating valid FQDN (%s): %w",
			hostname,
			ErrHostnameTooLong,
		)
	}

	return hostname, nil
}

// ValidateGivenName reports whether givenName is usable as a node's DNS label:
// a valid DNS label that, combined with baseDomain, yields an FQDN within
// MaxHostnameLength. Admin-facing write paths (e.g. node rename) reject names
// that fail this, since the mapper cannot build a map for a node — or any of
// its peers — whose GetFQDN fails. Derived paths sanitise/coerce instead.
func ValidateGivenName(givenName, baseDomain string) error {
	err := dnsname.ValidLabel(givenName)
	if err != nil {
		return fmt.Errorf("%q is not a valid DNS label: %w", givenName, err)
	}

	// Reuse GetFQDN so the length bound stays identical to what the mapper
	// enforces; a valid 63-char label can still overflow under a long
	// base_domain.
	_, err = (&Node{GivenName: givenName}).GetFQDN(baseDomain)
	if err != nil {
		return err
	}

	return nil
}

// AnnouncedRoutes returns the list of routes the node announces, as
// reported by the client in [tailcfg.Hostinfo.RoutableIPs]. Announcement alone
// does not grant visibility — see [Node.SubnetRoutes] for approval-gated
// access.
func (node *Node) AnnouncedRoutes() []netip.Prefix {

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Use a strict DNS label: 1-63 chars, letters/digits/hyphens only, no leading or trailing hyphen
  2. Sanitize free-text names before rename: lowercase, replace separators with hyphens, trim hyphens, cap at 63 chars
  3. Run ValidateGivenName in your tooling before submitting the rename so the error surfaces early with context

Example fix

// before
name := "file server (2)"
err := types.ValidateGivenName(name, baseDomain) // not a valid DNS label
// after
name := "file-server-2"
err := types.ValidateGivenName(name, baseDomain)
Defensive patterns

Strategy: validation

Validate before calling

import (
    "regexp"
    "headscale/hscontrol/types"
)

var dnsLabel = regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)

func safeGivenName(raw string) (string, error) {
    if !dnsLabel.MatchString(raw) {
        return "", fmt.Errorf("%q is not DNS-label safe", raw)
    }
    if err := types.ValidateGivenName(raw, baseDomain); err != nil {
        return "", err
    }
    return raw, nil
}

Try / catch

if err := types.ValidateGivenName(name, baseDomain); err != nil {
    // derive a sanitized fallback instead of persisting the bad name
    name = sanitizeToDNSLabel(name)
}

Prevention

When it happens

Trigger: Calling ValidateGivenName with names like "my machine" (space), "-lead", "trail-", "a"*64, "under_score", or "" — used in rename handlers and provisioning code before persisting a GivenName.

Common situations: Users renaming nodes via the CLI/API with hostnames containing spaces, underscores, or other non-DNS characters; automation scripts deriving node names from free-text asset labels.

Related errors


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