{"record":{"id":"0b87ef83c1e76759","repo":"juanfont/headscale","slug":"automigrating-types-node-w","errorCode":null,"errorMessage":"automigrating types.Node: %w","messagePattern":"automigrating types\\.Node: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"hscontrol/db/db.go","lineNumber":116,"sourceCode":"\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tRollback: func(db *gorm.DB) error { return nil },\n\t\t\t},\n\t\t\t// Add back constraint so you cannot delete preauth keys that\n\t\t\t// is still used by a node.\n\t\t\t{\n\t\t\t\tID: \"202501311657\",\n\t\t\t\tMigrate: func(tx *gorm.DB) error {\n\t\t\t\t\terr := tx.AutoMigrate(&types.PreAuthKey{})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"automigrating types.PreAuthKey: %w\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\terr = tx.AutoMigrate(&types.Node{})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"automigrating types.Node: %w\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tRollback: func(db *gorm.DB) error { return nil },\n\t\t\t},\n\t\t\t// Ensure there are no nodes referring to a deleted preauthkey.\n\t\t\t{\n\t\t\t\tID: \"202502070949\",\n\t\t\t\tMigrate: func(tx *gorm.DB) error {\n\t\t\t\t\tif tx.Migrator().HasTable(&types.PreAuthKey{}) {\n\t\t\t\t\t\terr := tx.Exec(`\nUPDATE nodes\nSET auth_key_id = NULL\nWHERE auth_key_id IS NOT NULL\nAND auth_key_id NOT IN (\n    SELECT id FROM pre_auth_keys\n);","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/db.go#L98-L134","documentation":"Migration 202501311657 failed at tx.AutoMigrate(&types.Node{}) - the second step that re-aligns the nodes table with the current Go struct (part of the same migration that restores the pre-auth-key FK). Failure means DDL on the nodes table was rejected: locks, permissions, or an incompatible existing column type.","triggerScenarios":"AutoMigrate attempts to alter nodes while another connection holds a lock; a nodes column type changed between versions in a way SQLite cannot remap; disk full during table rewrite (SQLite alters copy the table).","commonSituations":"Large nodes table with insufficient disk for SQLite's copy-on-alter; Postgres ACCESS EXCLUSIVE lock blocked by a long-running query; upgrading a database that a forked build modified.","solutions":["Free disk space at least equal to the nodes table size (SQLite needs room for the rewrite).","Terminate blocking sessions / stop the old headscale process before restarting.","Inspect the wrapped error for the specific column/DDL that failed and compare against types.Node.","Restore the pre-migration backup if the migration aborted mid-transaction."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := db.NewHeadscaleDatabase(cfg); err != nil {\n    if strings.Contains(err.Error(), \"automigrating types.Node\") {\n        // DDL on nodes failed: free disk (SQLite table rewrite), clear locks, restart\n    }\n}","preventionTips":["Reserve free disk at least the size of the nodes table before upgrading.","Terminate long-running queries against nodes before restarting headscale.","Avoid schema modifications from forks - they make AutoMigrate fail on upgrade."],"tags":["database","migration","gorm","node","sqlite"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}