juanfont/headscale · warning

node name is not unique

Error message

node name is not unique

What it means

Sentinel in hscontrol/db/node.go returned by RenameNode (node.go:200-201) and by the state layer (hscontrol/state/state.go:1054) when a node's given_name is already taken by another node. Given names are DNS labels under MagicDNS, so headscale enforces uniqueness before writing. API error mappers (hscontrol/api/v1/errors.go:32, v2/errors.go:77) map it to a conflict response.

Source

Thrown at hscontrol/db/node.go:32

	"github.com/juanfont/headscale/hscontrol/types"
	"github.com/juanfont/headscale/hscontrol/util"
	"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
	"github.com/rs/zerolog/log"
	"gorm.io/gorm"
	"tailscale.com/types/key"
	"tailscale.com/util/dnsname"
)

const (
	NodeGivenNameHashLength = 8
	NodeGivenNameTrimSize   = 2

	// defaultTestNodePrefix is the default hostname prefix for nodes created in tests.
	defaultTestNodePrefix = "testnode"
)

// ErrNodeNameNotUnique is returned when a node name is not unique.
var ErrNodeNameNotUnique = errors.New("node name is not unique")

// preloadNode returns a session that eager-loads a node's AuthKey, the
// AuthKey's User, and the node's User.
func preloadNode(tx *gorm.DB) *gorm.DB {
	return tx.
		Preload("AuthKey").
		Preload("AuthKey.User").
		Preload("User")
}

var (
	ErrNodeNotFound                  = errors.New("node not found")
	ErrNodeRouteIsNotAvailable       = errors.New("route is not available on node")
	ErrNodeNotFoundRegistrationCache = errors.New(
		"node not found in registration cache",
	)
	ErrCouldNotConvertNodeInterface = errors.New("failed to convert node interface")
)

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Pick a different name, or first rename/release the node currently holding it
  2. List nodes (`headscale nodes list`) to see existing given names before choosing
  3. Delete the stale node occupying the name if it is decommissioned

Example fix

# before
headscale nodes rename --identifier 7 --new-name laptop
# error: node name is not unique

# after
headscale nodes rename --identifier 7 --new-name laptop-2
Defensive patterns

Strategy: validation

Validate before calling

// before renaming, check uniqueness the same way the DB does
count, err := tx.Model(&types.Node{}).
    Where("given_name = ? AND id != ?", newName, nodeID).Count(&new(int64)).Error

Try / catch

if err := state.RenameNode(nodeID, newName); err != nil {
    if errors.Is(err, state.ErrNodeNameNotUnique) {
        // map to a 409-style conflict for API callers
        return conflict("given_name already in use: " + newName)
    }
    return err
}

Prevention

When it happens

Trigger: Calling `headscale nodes rename` (or the gRPC/API rename) with a name that another node already has as its given_name; concurrent registration of two nodes that normalize to the same given name.

Common situations: Renaming node B to node A's name to 'migrate' it; VM clones re-registering with identical hostnames so the generated given names collide during manual rename.

Related errors


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