juanfont/headscale · error · ErrNodeNameNotUnique
node name is not unique
Error message
node name is not unique
What it means
ErrNodeNameNotUnique is a sentinel error in hscontrol/state/state.go:78 (mirrored in hscontrol/db/node.go:32) returned when setting a node's GivenName would collide with another node's unique name. It is wrapped with the offending name at state.go:1054 and returned from db uniqueness checks (db/node.go:201); API layers map it to a conflict response (v1/errors.go:32, v2/errors.go:77).
Source
Thrown at hscontrol/state/state.go:78
// defaultNodeStoreBatchTimeout is the default maximum time to wait before
// processing a partial batch of node operations.
defaultNodeStoreBatchTimeout = 500 * time.Millisecond
)
// ErrUnsupportedPolicyMode is returned for invalid policy modes. Valid modes are "file" and "db".
var ErrUnsupportedPolicyMode = errors.New("unsupported policy mode")
// ErrNodeNotFound is returned when a node cannot be found by its ID.
var ErrNodeNotFound = errors.New("node not found")
// ErrInvalidNodeView is returned when an invalid node view is provided.
var ErrInvalidNodeView = errors.New("invalid node view provided")
// ErrNodeNotInNodeStore is returned when a node no longer exists in the [NodeStore].
var ErrNodeNotInNodeStore = errors.New("node no longer exists in NodeStore")
// ErrNodeNameNotUnique is returned when a node name is not unique.
var ErrNodeNameNotUnique = errors.New("node name is not unique")
// nodeUpdateColumns lists all Node columns that should be written
// during a struct-based GORM Updates() call. Listing them explicitly
// forces GORM to include nil/zero-value fields (e.g. UserID=nil when
// converting a user-owned node to tagged) that struct-based Updates()
// would otherwise silently skip.
//
// Excluded columns:
// - AuthKeyID, AuthKey: prevents GORM from persisting stale
// PreAuthKey references after a key has been deleted (#2862).
// - User: GORM association, not a real column.
// - IsOnline: runtime-only field (gorm:"-").
//
// Expiry is included here but may be omitted at call sites that must
// not touch it (see persistNodeToDB).
var nodeUpdateColumns = []string{
"MachineKey",
"NodeKey",View on GitHub (pinned to 565fd254d0)
Solutions
- Choose a different GivenName that no other node currently uses
- Rename or delete the existing node holding the name first
- Give cloned machines unique hostnames before they register
- Check uniqueness beforehand by listing all nodes and comparing names
Example fix
# before headscale node rename -i 7 -s node-1 # node-1 already taken # after headscale node rename -i 7 -s node-1-b
Defensive patterns
Strategy: validation
Validate before calling
// Before renaming, confirm the target name is unused
for _, n := range listNodes() {
if n.GivenName == newName && n.ID != targetID {
return fmt.Errorf("name %q taken", newName)
}
} Type guard
null
Try / catch
if errors.Is(err, state.ErrNodeNameNotUnique) { /* prompt for another name or append a suffix */ } Prevention
- Give imaged/cloned machines unique hostnames before registration
- Check name uniqueness in UIs/scripts before submitting a rename
When it happens
Trigger: Calling headscale node rename / SetGivenName with a hostname another node already owns; registration flows normalizing a hostname to a value that collides; manually forcing a name that duplicates an existing node.
Common situations: Cloning VMs/containers with identical hostnames so two registrations want the same GivenName; renaming via API without checking the target name is free; name normalization stripping disambiguating suffixes.
Related errors
- node name is not unique
- node not found
- node not found
- route is not available on node
- failed to convert node interface
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/5716892da96d6bac.
Report an issue: GitHub.