MHSanaei/3x-ui · error
open sqlite source: %w
Error message
open sqlite source: %w
What it means
MigrateData opens the SQLite source via gorm with WAL + 10s busy_timeout and wraps a failed open. The file exists (earlier Stat passed) but could not be opened as a usable SQLite DB: corrupt header, unreadable permissions, or a driver-level failure. The wrapped error distinguishes these.
Source
Thrown at internal/database/migrate_data.go:82
// (re)created with AutoMigrate; truncate and copy then run in one transaction,
// so a failed migration leaves the destination data unchanged. Source data is
// left untouched.
func MigrateData(srcPath, dstDSN string) error {
if _, err := os.Stat(srcPath); err != nil {
return fmt.Errorf("source sqlite not found at %s: %w", srcPath, err)
}
if dstDSN == "" {
return errors.New("destination DSN is required")
}
if err := os.MkdirAll(path.Dir(srcPath), 0o755); err != nil {
return err
}
srcDSN := srcPath + "?_journal_mode=WAL&_busy_timeout=10000"
src, err := gorm.Open(sqlite.Open(srcDSN), &gorm.Config{Logger: logger.Discard})
if err != nil {
return fmt.Errorf("open sqlite source: %w", err)
}
srcSQL, err := src.DB()
if err != nil {
return err
}
defer srcSQL.Close()
dst, err := gorm.Open(postgres.Open(dstDSN), &gorm.Config{Logger: logger.Discard})
if err != nil {
return fmt.Errorf("open postgres destination: %w", err)
}
dstSQL, err := dst.DB()
if err != nil {
return err
}
defer dstSQL.Close()
dstSQL.SetConnMaxLifetime(time.Hour)
View on GitHub (pinned to ad32144c42)
Solutions
- Run `sqlite3 <src> 'PRAGMA integrity_check;'` to verify the file is a sound database.
- Fix ownership/permissions so the panel process can read (and briefly write, for WAL) the file.
- Re-copy the DB together with any -wal/-shm sidecars, or better: produce it with BackupSQLite and migrate from that snapshot.
Defensive patterns
Strategy: try-catch
Validate before calling
if err := verifySQLiteFile(srcPath); err != nil { // runs PRAGMA integrity_check via a throwaway handle
return err
} Try / catch
if err := database.MigrateData(src, dsn); err != nil {
if strings.Contains(err.Error(), "open sqlite source") {
// exists but unusable: check perms/corruption, re-copy from BackupSQLite output
}
return err
} Prevention
- Copy the DB together with -wal/-shm sidecars, or snapshot it with BackupSQLite and migrate from the snapshot.
- Ensure the migrating process has read (and WAL write) permission on the source directory.
When it happens
Trigger: srcPath exists but is a truncated/corrupt DB (interrupted copy, sparse transfer); file owned by another user without read permission; path is a directory; WAL sidecar files (-wal/-shm) missing after an unclean copy while the DB depended on them.
Common situations: Copying x-ui.db mid-write without VACUUM INTO/backup API; rsync excluding -wal/-shm; permissions after moving files between users/containers.
Related errors
- open sqlite destination: %w
- sqlite integrity check failed: %s
- sqlite backup destination already exists: %s
- source sqlite not found at %s: %w
- destination already exists: %s
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/e771e8097e9d2b90.
Report an issue: GitHub.