juanfont/headscale · error
checking name uniqueness: %w
Error message
checking name uniqueness: %w
What it means
RenameNode counts existing nodes with the target given_name to enforce uniqueness before writing. This error is the COUNT query itself failing at the database level (not a duplicate — that returns ErrNodeNameNotUnique). The underlying driver error is in %w.
Source
Thrown at hscontrol/db/node.go:197
func SetLastSeen(tx *gorm.DB, nodeID types.NodeID, lastSeen time.Time) error {
return tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("last_seen", lastSeen).Error
}
// RenameNode takes a [types.Node] struct and a new [types.Node.GivenName] for the nodes
// and renames it. Validation should be done in the state layer before calling this function.
func RenameNode(tx *gorm.DB,
nodeID types.NodeID, newName string,
) error {
err := dnsname.ValidLabel(newName)
if err != nil {
return fmt.Errorf("renaming node: %w", err)
}
// Check if the new name is unique
var count int64
if err := tx.Model(&types.Node{}).Where("given_name = ? AND id != ?", newName, nodeID).Count(&count).Error; err != nil { //nolint:noinlineerr
return fmt.Errorf("checking name uniqueness: %w", err)
}
if count > 0 {
return ErrNodeNameNotUnique
}
if err := tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("given_name", newName).Error; err != nil { //nolint:noinlineerr
return fmt.Errorf("renaming node in database: %w", err)
}
return nil
}
func (hsdb *HSDatabase) NodeSetExpiry(nodeID types.NodeID, expiry *time.Time) error {
return hsdb.Write(func(tx *gorm.DB) error {
return NodeSetExpiry(tx, nodeID, expiry)
})
}View on GitHub (pinned to 565fd254d0)
Solutions
- Unwrap the error to distinguish lock/connectivity/schema causes
- For SQLite contention, retry after the competing write completes or enable busy_timeout
- Run pending migrations and verify the nodes table schema
- Retry the rename once the database is healthy
Defensive patterns
Strategy: retry
Try / catch
if err := db.RenameNode(tx, nodeID, name); err != nil {
if isTransientDBError(err) {
// retry after competing write completes
}
return err
} Prevention
- Batch renames instead of parallel hammering on SQLite
- Keep migrations current so the schema matches queries
When it happens
Trigger: SQLite database locked by a concurrent rename/registration; Postgres connection reset; schema drift where given_name column is missing after a botched migration.
Common situations: Bulk renames via automation hitting SQLite write contention; interrupted upgrade leaving the schema half-migrated.
Related errors
- registering existing node in database: %w
- saving API key to database: %w
- automigrating types.Route: %w
- automigrating types.Node: %w
- adding prefix column: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/0a03e0ebcdb5ecbb.
Report an issue: GitHub.