juanfont/headscale · error
renaming node in database: %w
Error message
renaming node in database: %w
What it means
The final step of RenameNode: an UPDATE of given_name for the node id. Failure is a database-level error — uniqueness race (another node took the name between the COUNT and UPDATE), lock, or the node id no longer existing. The uniqueness pre-check and update are not in a serializable snapshot, so a rare race can surface a constraint violation here.
Source
Thrown at hscontrol/db/node.go:205
) 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)
})
}
// NodeSetExpiry sets a new expiry time for a node.
// If expiry is nil, the node's expiry is disabled (node will never expire).
func NodeSetExpiry(tx *gorm.DB, nodeID types.NodeID, expiry *time.Time) error {
return tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("expiry", expiry).Error
}
func (hsdb *HSDatabase) DeleteNode(node *types.Node) error {View on GitHub (pinned to 565fd254d0)
Solutions
- Unwrap to check for a unique-constraint violation and map it to ErrNodeNameNotUnique semantics for the client
- Serialize renames (single writer) or retry the whole rename on constraint races
- Verify the node still exists before retrying
Defensive patterns
Strategy: try-catch
Try / catch
if err := db.RenameNode(tx, nodeID, name); err != nil {
if errors.Is(err, db.ErrNodeNameNotUnique) || isUniqueViolation(err) {
return ErrNameTaken // surface as 409
}
return err
} Prevention
- Treat unique-violation here the same as ErrNodeNameNotUnique
- Serialize rename operations through one queue
- Include the node id in logs to correlate races
When it happens
Trigger: Two concurrent renames to the same name racing past the COUNT check; node deleted between check and update; DB lock/timeout.
Common situations: Automation renaming many nodes in parallel; API retry storms.
Related errors
- saving node(%d) after adding IPs: %w
- renaming node: %w
- checking name uniqueness: %w
- registering existing node in database: %w
- saving node to database: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/b9a1e9631577c72f.
Report an issue: GitHub.