{"record":{"id":"783d4e2cd58692d5","repo":"juanfont/headscale","slug":"restoring-foreign-keys-w","errorCode":null,"errorMessage":"restoring foreign keys: %w","messagePattern":"restoring foreign keys: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/db.go","lineNumber":1179,"sourceCode":"\tif cfg.Type == types.DatabaseSqlite {\n\t\t// SQLite: Run the early migrations that GORM cannot handle safely with\n\t\t// foreign keys enabled (route and pre-auth-key automigrations) with FK\n\t\t// disabled, then run everything else with FK enabled.\n\t\t//\n\t\t// NO NEW MIGRATIONS SHOULD RUN WITH FK DISABLED. As of 2025-07-02, all\n\t\t// new migrations must run with foreign keys enabled via the\n\t\t// migrations.Migrate() call below.\n\t\tif err := dbConn.Exec(\"PRAGMA foreign_keys = OFF\").Error; err != nil { //nolint:noinlineerr\n\t\t\treturn fmt.Errorf(\"disabling foreign keys: %w\", err)\n\t\t}\n\n\t\t// Run up to and including the last migration that requires FK disabled.\n\t\tif err := migrations.MigrateTo(\"202501311657\"); err != nil { //nolint:noinlineerr\n\t\t\treturn fmt.Errorf(\"running migration 202501311657: %w\", err)\n\t\t}\n\n\t\tif err := dbConn.Exec(\"PRAGMA foreign_keys = ON\").Error; err != nil { //nolint:noinlineerr\n\t\t\treturn fmt.Errorf(\"restoring foreign keys: %w\", err)\n\t\t}\n\n\t\t// Run the rest of the migrations\n\t\tif err := migrations.Migrate(); err != nil { //nolint:noinlineerr\n\t\t\treturn err\n\t\t}\n\n\t\t// Check for constraint violations at the end\n\t\ttype constraintViolation struct {\n\t\t\tTable           string\n\t\t\tRowID           int\n\t\t\tParent          string\n\t\t\tConstraintIndex int\n\t\t}\n\n\t\tvar violatedConstraints []constraintViolation\n\n\t\trows, err := dbConn.Raw(\"PRAGMA foreign_key_check\").Rows()","sourceCodeStart":1161,"sourceCodeEnd":1197,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/db.go#L1161-L1197","documentation":"After the FK-off early-migration phase on sqlite, headscale re-enables enforcement with PRAGMA foreign_keys = ON before running all remaining migrations. This error means the re-enable statement failed — almost always the same lock/connection problem as error 408, hitting at a slightly later point in startup.","triggerScenarios":"Connection degraded or the database got locked between the early migrations finishing and the pragma being re-enabled (e.g. litestream grabbing an exclusive lock, disk filling up mid-startup).","commonSituations":"Same class as error 408: concurrent writers on the sqlite file, hardware/disk issues, or a container whose volume goes away during startup.","solutions":["Eliminate concurrent access to the sqlite file (single headscale instance, replication-only litestream).","Check disk space and filesystem health on the volume hosting the DB.","Restart headscale once the conflicting holder is gone — migrations are idempotent and will resume."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Same handling as error 408: if lock-related, remove the conflicting\n// holder and restart; migrations resume idempotently.","preventionTips":["Keep single-writer discipline on the sqlite file.","Monitor disk space — pragma execution can fail on a full volume."],"tags":["database","sqlite","pragma","locking"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}