{"record":{"id":"e771e8097e9d2b90","repo":"MHSanaei/3x-ui","slug":"open-sqlite-source-w","errorCode":null,"errorMessage":"open sqlite source: %w","messagePattern":"open sqlite source: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/database/migrate_data.go","lineNumber":82,"sourceCode":"// (re)created with AutoMigrate; truncate and copy then run in one transaction,\n// so a failed migration leaves the destination data unchanged. Source data is\n// left untouched.\nfunc MigrateData(srcPath, dstDSN string) error {\n\tif _, err := os.Stat(srcPath); err != nil {\n\t\treturn fmt.Errorf(\"source sqlite not found at %s: %w\", srcPath, err)\n\t}\n\tif dstDSN == \"\" {\n\t\treturn errors.New(\"destination DSN is required\")\n\t}\n\n\tif err := os.MkdirAll(path.Dir(srcPath), 0o755); err != nil {\n\t\treturn err\n\t}\n\n\tsrcDSN := srcPath + \"?_journal_mode=WAL&_busy_timeout=10000\"\n\tsrc, err := gorm.Open(sqlite.Open(srcDSN), &gorm.Config{Logger: logger.Discard})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open sqlite source: %w\", err)\n\t}\n\tsrcSQL, err := src.DB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer srcSQL.Close()\n\n\tdst, err := gorm.Open(postgres.Open(dstDSN), &gorm.Config{Logger: logger.Discard})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open postgres destination: %w\", err)\n\t}\n\tdstSQL, err := dst.DB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer dstSQL.Close()\n\tdstSQL.SetConnMaxLifetime(time.Hour)\n","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/migrate_data.go#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"Copying x-ui.db mid-write without VACUUM INTO/backup API; rsync excluding -wal/-shm; permissions after moving files between users/containers.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if err := verifySQLiteFile(srcPath); err != nil { // runs PRAGMA integrity_check via a throwaway handle\n    return err\n}","typeGuard":null,"tryCatchPattern":"if err := database.MigrateData(src, dsn); err != nil {\n    if strings.Contains(err.Error(), \"open sqlite source\") {\n        // exists but unusable: check perms/corruption, re-copy from BackupSQLite output\n    }\n    return err\n}","preventionTips":["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."],"tags":["database","sqlite","migration","corruption","filesystem"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}