juanfont/headscale · critical
adding column types.Node: %w
Error message
adding column types.Node: %w
What it means
Migration 202502131714 (moving routes from the routes table into nodes.approved_routes) failed adding the approved_routes column to nodes. DDL failure at this step is environmental: lock contention, missing ALTER privilege, insufficient disk for SQLite's table rewrite, or the column existing with an incompatible type (guarded by HasColumn, so re-runs are safe).
Source
Thrown at hscontrol/db/db.go:154
if err != nil {
return fmt.Errorf("setting auth_key to null on nodes with non-existing keys: %w", err)
}
}
return nil
},
Rollback: func(db *gorm.DB) error { return nil },
},
// v0.26.0
// Migrate all routes from the Route table to the new field ApprovedRoutes
// in the Node table. Then drop the Route table.
{
ID: "202502131714",
Migrate: func(tx *gorm.DB) error {
if !tx.Migrator().HasColumn(&types.Node{}, "approved_routes") {
err := tx.Migrator().AddColumn(&types.Node{}, "approved_routes")
if err != nil {
return fmt.Errorf("adding column types.Node: %w", err)
}
}
nodeRoutes := map[uint64][]netip.Prefix{}
var routes []types.Route //nolint:staticcheck // SA1019: Route kept for migrations
err = tx.Find(&routes).Error
if err != nil {
return fmt.Errorf("fetching routes: %w", err)
}
for _, route := range routes {
if route.Enabled {
nodeRoutes[route.NodeID] = append(nodeRoutes[route.NodeID], route.Prefix)
}
}
View on GitHub (pinned to 565fd254d0)
Solutions
- Free disk space and stop concurrent database users, then restart headscale.
- Grant ALTER on the database / UPDATE on nodes to the migration role.
- Check pg_stat_activity or lsof on the SQLite file for lock holders.
- If it failed mid-transaction, the rollback is automatic - simply retry after fixing the environment.
Defensive patterns
Strategy: try-catch
Try / catch
if _, err := db.NewHeadscaleDatabase(cfg); err != nil {
if strings.Contains(err.Error(), "adding column types.Node") {
// ALTER TABLE failed: clear locks, free disk, restart - HasColumn guard makes reruns safe
}
} Prevention
- Back up before upgrading past v0.26.0.
- Grant ALTER privileges on the nodes table to the migration role.
- Free disk proportional to the nodes table size for SQLite DDL.
When it happens
Trigger: ALTER TABLE nodes ADD COLUMN approved_routes rejected because another session locks nodes, the Postgres role lacks ALTER, or disk is full mid-rewrite.
Common situations: Large nodes table and low disk during SQLite migration; running the server while a long analytics query holds locks; restrictive DB grants in managed Postgres.
Related errors
- dropping routes table: %w
- renaming table %s to %s_old: %w
- creating new table: %w
- foreign key constraints violated
- automigrating types.Route: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/2a426d5851fe6199.
Report an issue: GitHub.