{"record":{"id":"5716892da96d6bac","repo":"juanfont/headscale","slug":"node-name-is-not-unique-571689","errorCode":null,"errorMessage":"node name is not unique","messagePattern":"node name is not unique","errorType":"http","errorClass":"ErrNodeNameNotUnique","httpStatus":400,"severity":"error","filePath":"hscontrol/state/state.go","lineNumber":78,"sourceCode":"\t// defaultNodeStoreBatchTimeout is the default maximum time to wait before\n\t// processing a partial batch of node operations.\n\tdefaultNodeStoreBatchTimeout = 500 * time.Millisecond\n)\n\n// ErrUnsupportedPolicyMode is returned for invalid policy modes. Valid modes are \"file\" and \"db\".\nvar ErrUnsupportedPolicyMode = errors.New(\"unsupported policy mode\")\n\n// ErrNodeNotFound is returned when a node cannot be found by its ID.\nvar ErrNodeNotFound = errors.New(\"node not found\")\n\n// ErrInvalidNodeView is returned when an invalid node view is provided.\nvar ErrInvalidNodeView = errors.New(\"invalid node view provided\")\n\n// ErrNodeNotInNodeStore is returned when a node no longer exists in the [NodeStore].\nvar ErrNodeNotInNodeStore = errors.New(\"node no longer exists in NodeStore\")\n\n// ErrNodeNameNotUnique is returned when a node name is not unique.\nvar ErrNodeNameNotUnique = errors.New(\"node name is not unique\")\n\n// nodeUpdateColumns lists all Node columns that should be written\n// during a struct-based GORM Updates() call.  Listing them explicitly\n// forces GORM to include nil/zero-value fields (e.g. UserID=nil when\n// converting a user-owned node to tagged) that struct-based Updates()\n// would otherwise silently skip.\n//\n// Excluded columns:\n//   - AuthKeyID, AuthKey: prevents GORM from persisting stale\n//     PreAuthKey references after a key has been deleted (#2862).\n//   - User: GORM association, not a real column.\n//   - IsOnline: runtime-only field (gorm:\"-\").\n//\n// Expiry is included here but may be omitted at call sites that must\n// not touch it (see persistNodeToDB).\nvar nodeUpdateColumns = []string{\n\t\"MachineKey\",\n\t\"NodeKey\",","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/state/state.go#L60-L96","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nheadscale node rename -i 7 -s node-1   # node-1 already taken\n\n# after\nheadscale node rename -i 7 -s node-1-b","handlingStrategy":"validation","validationCode":"// Before renaming, confirm the target name is unused\nfor _, n := range listNodes() {\n    if n.GivenName == newName && n.ID != targetID {\n        return fmt.Errorf(\"name %q taken\", newName)\n    }\n}","typeGuard":"null","tryCatchPattern":"if errors.Is(err, state.ErrNodeNameNotUnique) { /* prompt for another name or append a suffix */ }","preventionTips":["Give imaged/cloned machines unique hostnames before registration","Check name uniqueness in UIs/scripts before submitting a rename"],"tags":["headscale","node","naming","conflict","api"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}