juanfont/headscale · critical
loading users for RequestTags migration: %w
Error message
loading users for RequestTags migration: %w
What it means
Migration '202511131445' (RequestTags migration, following the forced_tags rename) fails calling ListUsers(tx, nil) while loading all users to construct a PolicyManager that validates historical --advertise-tags values. The wrapped error is a database query failure: schema/type mismatch on the users table, permission problem, or connection loss mid-migration.
Source
Thrown at hscontrol/db/db.go:622
// Fixes: https://github.com/juanfont/headscale/issues/3006
ID: "202601121700-migrate-hostinfo-request-tags",
Migrate: func(tx *gorm.DB) error {
// 1. Load policy from file or database based on configuration
policyData, err := PolicyBytes(tx, cfg)
if err != nil {
log.Warn().Err(err).Msg("failed to load policy, skipping RequestTags migration (tags will be validated on node reconnect)")
return nil
}
if len(policyData) == 0 {
log.Info().Msg("no policy found, skipping RequestTags migration (tags will be validated on node reconnect)")
return nil
}
// 2. Load users and nodes to create PolicyManager
users, err := ListUsers(tx, nil)
if err != nil {
return fmt.Errorf("loading users for RequestTags migration: %w", err)
}
nodes, err := ListNodes(tx)
if err != nil {
return fmt.Errorf("loading nodes for RequestTags migration: %w", err)
}
// 3. Create PolicyManager (handles HuJSON parsing, groups, nested tags, etc.)
polMan, err := policy.NewPolicyManager(policyData, users, nodes.ViewSlice())
if err != nil {
log.Warn().Err(err).Msg("failed to parse policy, skipping RequestTags migration (tags will be validated on node reconnect)")
return nil
}
// 4. Process each node
for _, node := range nodes {
if node.Hostinfo == nil {
continueView on GitHub (pinned to 565fd254d0)
Solutions
- Check the wrapped error - a SQL 'column does not exist' means schema drift: verify migrations table ordering and reconcile (see the forced_tags rename guidance)
- Ensure the migration runs with the database idle (stop other headscale/admin clients during upgrade)
- For flaky Postgres connectivity, fix the network/pool settings (conn max lifetime) and restart headscale so the migration transaction retries from the top
- Restore from backup if the schema/history mismatch cannot be reconciled
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: schema and connectivity are sane before upgrade
if err := db.Ping(); err != nil { log.Fatalf("db unreachable: %v", err) }
if _, err := db.Exec("SELECT 1 FROM users LIMIT 1"); err != nil {
log.Fatalf("users unreadable, migration will fail: %v", err)
} Prevention
- Keep the schema and the migrations bookkeeping table backed up together (pg_dump includes both)
- Avoid schema drift: only headscale itself should issue DDL against its database
- Monitor DB connection stability before scheduling an upgrade
When it happens
Trigger: The users table schema does not match what ListUsers expects (schema drift from out-of-order upgrades), the migration runs on a Postgres connection that drops, or the SELECT hits a lock timeout while another transaction holds users.
Common situations: Upgrading a database that previously ran mixed headscale versions; long-running admin transactions blocking the migration read; Postgres restart or failover during startup.
Related errors
- renaming forced_tags to tags: %w
- loading nodes for RequestTags migration: %w
- foreign key constraints violated
- is not valid
- user not found
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/a9e88fe10ed18e32.
Report an issue: GitHub.