{"record":{"id":"856eb84e359e0c33","repo":"MHSanaei/3x-ui","slug":"not-a-3x-ui-panel-database-required-table-q-is-m","errorCode":null,"errorMessage":"not a 3x-ui panel database: required table %q is missing","messagePattern":"not a 3x-ui panel database: required table %q is missing","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/database/migrate_data.go","lineNumber":336,"sourceCode":"// PrepareSQLiteForMigration rejects SQLite files that are not a panel database\n// before the caller causes any downtime, then AutoMigrates the panel schema\n// onto the file so backups from older versions gain the newer tables and\n// columns the row copy reads. Data-level upgrades are not needed here: they\n// run dialect-agnostically on the destination via InitDB after the import.\nfunc PrepareSQLiteForMigration(dbPath string) error {\n\tgdb, err := gorm.Open(sqlite.Open(dbPath+\"?_busy_timeout=10000\"), &gorm.Config{Logger: logger.Discard})\n\tif err != nil {\n\t\treturn err\n\t}\n\tsqlDB, err := gdb.DB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer sqlDB.Close()\n\n\tfor _, table := range []string{\"users\", \"settings\", \"inbounds\"} {\n\t\tif !sqliteTableExists(sqlDB, table) {\n\t\t\treturn fmt.Errorf(\"not a 3x-ui panel database: required table %q is missing\", table)\n\t\t}\n\t}\n\tfor _, m := range migrationModels() {\n\t\tif err := gdb.AutoMigrate(m); err != nil && !isIgnorableDuplicateColumnErr(gdb, err, m) {\n\t\t\treturn fmt.Errorf(\"upgrade panel schema for %T: %w\", m, err)\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":318,"sourceCodeEnd":346,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/migrate_data.go#L318-L346","documentation":"Thrown by PrepareSQLiteForMigration, the pre-flight check of the SQLite source file: it opens the file and requires the core panel tables users, settings and inbounds to exist. If any is missing the file is declared not a panel database before any downtime or copying happens. Key trap: gorm.Open(sqlite.Open(path)) silently CREATES an empty database when the path does not exist, so a typo'd path reliably produces this error.","triggerScenarios":"Passing a wrong/nonexistent file path (an empty DB is created, no tables); passing some other SQLite file (e.g. the sub-server DB or an unrelated app); a truncated/corrupt backup; an empty 0-byte backup file.","commonSituations":"Shell glob or variable expansion producing a wrong path; restoring a backup that failed mid-transfer; pointing the migration at the subscription-server DB instead of the panel DB; the file being created by a previous failed run at the same wrong path.","solutions":["Verify the path and inspect the file: sqlite3 /path/x-ui.db '.tables' — users, settings, inbounds must be listed.","Delete any empty SQLite file the failed run created at the wrong path, then pass the real backup path.","If the backup is 0 bytes or truncated, re-take it from the source panel.","Confirm you selected the panel DB (commonly /etc/x-ui/x-ui.db), not another *.db in the same directory."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify the file is a panel DB before calling migration entry points\ndb, err := sql.Open(\"sqlite\", dbPath)\nif err != nil { return err }\ndefer db.Close()\nfor _, t := range []string{\"users\", \"settings\", \"inbounds\"} {\n    var one int\n    if db.QueryRow(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?\", t).Scan(&one) != nil {\n        return fmt.Errorf(\"%s lacks table %s — not a panel DB\", dbPath, t)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass a COPY of the panel DB, never the only original (prepare mutates it via AutoMigrate).","Delete zero-byte files a failed run may have created at a wrong path.","Confirm the path with 'sqlite3 <file> .tables' before migrating."],"tags":["database","sqlite","migration","validation","file-path"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}