MHSanaei/3x-ui · error
not a 3x-ui panel database: required table %q is missing
Error message
not a 3x-ui panel database: required table %q is missing
What it means
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.
Source
Thrown at internal/database/migrate_data.go:336
// PrepareSQLiteForMigration rejects SQLite files that are not a panel database
// before the caller causes any downtime, then AutoMigrates the panel schema
// onto the file so backups from older versions gain the newer tables and
// columns the row copy reads. Data-level upgrades are not needed here: they
// run dialect-agnostically on the destination via InitDB after the import.
func PrepareSQLiteForMigration(dbPath string) error {
gdb, err := gorm.Open(sqlite.Open(dbPath+"?_busy_timeout=10000"), &gorm.Config{Logger: logger.Discard})
if err != nil {
return err
}
sqlDB, err := gdb.DB()
if err != nil {
return err
}
defer sqlDB.Close()
for _, table := range []string{"users", "settings", "inbounds"} {
if !sqliteTableExists(sqlDB, table) {
return fmt.Errorf("not a 3x-ui panel database: required table %q is missing", table)
}
}
for _, m := range migrationModels() {
if err := gdb.AutoMigrate(m); err != nil && !isIgnorableDuplicateColumnErr(gdb, err, m) {
return fmt.Errorf("upgrade panel schema for %T: %w", m, err)
}
}
return nil
}
View on GitHub (pinned to ad32144c42)
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.
Defensive patterns
Strategy: validation
Validate before calling
// verify the file is a panel DB before calling migration entry points
db, err := sql.Open("sqlite", dbPath)
if err != nil { return err }
defer db.Close()
for _, t := range []string{"users", "settings", "inbounds"} {
var one int
if db.QueryRow("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", t).Scan(&one) != nil {
return fmt.Errorf("%s lacks table %s — not a panel DB", dbPath, t)
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- source sqlite not found at %s: %w
- open sqlite source: %w
- copy %T: %w
- open sqlite destination: %w
- upgrade panel schema for %T: %w
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/856eb84e359e0c33.
Report an issue: GitHub.