juanfont/headscale · error · ErrGivenNameTaken
given name already in use by another node
Error message
given name already in use by another node
What it means
ErrGivenNameTaken is returned by NodeStore.SetGivenName (hscontrol/state/node_store.go:507) when renaming a node to a DNS label that another node in the current snapshot already holds (case-sensitive GivenName equality across all other node IDs). Given names must be unique per tailnet for MagicDNS to resolve unambiguously; unlike registration-time name clashes, renames never auto-suffix. State.RenameNode (state.go:1053) rewraps it as ErrNodeNameNotUnique for API/CLI callers.
Source
Thrown at hscontrol/state/node_store.go:32
"github.com/juanfont/headscale/hscontrol/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"tailscale.com/net/tsaddr"
"tailscale.com/types/key"
"tailscale.com/types/views"
"tailscale.com/util/dnsname"
)
// fallbackGivenName is the DNS label used when a node is written with
// an empty [types.Node.GivenName]. Matches Tailscale SaaS behaviour
// for empty sanitised labels.
const fallbackGivenName = "node"
// Errors returned by [NodeStore.SetGivenName]. [ErrNodeNotFound] is defined
// in state.go and reused here.
var (
ErrGivenNameTaken = errors.New("given name already in use by another node")
ErrGivenNameInvalid = errors.New("given name is not a valid DNS label")
)
const (
put = 1
del = 2
rebuildPeerMaps = 4
setName = 5
updateMulti = 6
)
const prometheusNamespace = "headscale"
var (
nodeStoreOperations = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: prometheusNamespace,
Name: "nodestore_operations_total",
Help: "Total number of NodeStore operations",View on GitHub (pinned to 565fd254d0)
Solutions
- Pick a different, unused DNS label (list nodes first: headscale node list to see current names)
- Or rename/free the node currently holding the label first, then retry
- Catch ErrNodeNameNotUnique in callers and surface a user-facing 'name taken' message rather than retrying
- Remember comparison is exact — 'Laptop' and 'laptop' differ; check case
Example fix
// before headscale node rename -i 2 laptop // node 1 already is "laptop" // after headscale node list // verify taken names headscale node rename -i 2 laptop2
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check name availability before renaming
func nameFree(store *state.NodeStore, id types.NodeID, name string) bool {
for _, n := range store.Snapshot().Nodes() { // any read-only node listing
if n.ID() != id && n.GivenName() == name { return false }
}
return true
} Try / catch
view, change, err := st.RenameNode(nodeID, newName)
if err != nil {
if errors.Is(err, state.ErrNodeNameNotUnique) { // wraps ErrGivenNameTaken
// prompt the admin for a different label; do NOT retry unchanged
}
if errors.Is(err, state.ErrGivenNameInvalid) {
// fix the label: must be a valid DNS label fitting the FQDN limit
}
} Prevention
- List node names (headscale node list) before choosing a new label
- Remember names are per-tailnet unique and compared exactly (case-sensitive)
- Rename is idempotent for a node's own current name but never auto-suffixes on collision
- Surface name-taken errors to the admin UI instead of retrying automatically
When it happens
Trigger: State.RenameNode(nodeID, name) / headscale node rename CLI where any other node's GivenName equals the requested label exactly (node_store.go:499-504 scans all nodes, skipping the node's own ID). Renaming a node to its own current name is idempotent and allowed.
Common situations: Two machines registered with the same hostname (the second usually got an auto-suffixed name) and an admin renaming one onto the other's label; renaming to a name later taken by a newly registered node; retrying a rename after partial failures without checking current state.
Related errors
- %q is not a valid DNS label: %w
- node name is not unique
- renaming node: %w
- ErrHostnameTooLong
- missing parameters
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/0aa5a900f1e5303d.
Report an issue: GitHub.