{"record":{"id":"5f4827fb54e40f1f","repo":"MHSanaei/3x-ui","slug":"destination-already-exists-s","errorCode":null,"errorMessage":"destination already exists: %s","messagePattern":"destination already exists: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/database/dump_sqlite.go","lineNumber":118,"sourceCode":"\tif err := rows2.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tb.WriteString(\"COMMIT;\\n\")\n\n\treturn []byte(b.String()), nil\n}\n\n// RestoreSQLite rebuilds a SQLite database at dstPath from a SQL text dump\n// produced by DumpSQLite (or `sqlite3 .dump`). dstPath must not already exist so\n// an existing database is never clobbered silently.\nfunc RestoreSQLite(dumpPath, dstPath string) error {\n\tscript, err := os.ReadFile(dumpPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := os.Stat(dstPath); err == nil {\n\t\treturn fmt.Errorf(\"destination already exists: %s\", dstPath)\n\t}\n\n\tgdb, err := gorm.Open(sqlite.Open(dstPath), &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\n\t// mattn/go-sqlite3 executes every statement in a multi-statement string.\n\tif _, err := sqlDB.ExecContext(context.Background(), string(script)); err != nil {\n\t\tsqlDB.Close()\n\t\tos.Remove(dstPath)\n\t\treturn fmt.Errorf(\"restore failed: %w\", err)\n\t}\n\treturn sqlDB.Close()","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/dump_sqlite.go#L100-L136","documentation":"RestoreSQLite rebuilds a database at dstPath from a SQL dump and intentionally refuses when dstPath already exists, so a live/old database is never silently clobbered. The check runs after reading the dump script but before opening the destination. It is a safety stop, not corruption.","triggerScenarios":"Restoring over the panel's current DB file without moving it aside first; running the restore command twice; targeting a path where an old DB remains.","commonSituations":"Disaster-recovery runbooks skipping the 'move old db away' step; migration rollback attempts landing on the original filename.","solutions":["Move/rename the existing dstPath first (e.g. mv x-ui.db x-ui.db.bak), then restore.","Or restore to a fresh path and repoint the panel (XUI_DB_DSN / file location) at it.","Verify the dump source is the intended one before deleting anything."],"exampleFix":"# before\nRestoreSQLite(\"dump.sql\", \"/etc/x-ui/x-ui.db\")\n# after\nmv /etc/x-ui/x-ui.db /etc/x-ui/x-ui.db.bak\nRestoreSQLite(\"dump.sql\", \"/etc/x-ui/x-ui.db\")","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(dstPath); err == nil {\n    return fmt.Errorf(\"destination exists; move it aside first: %s\", dstPath)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Restore runbooks must move the old DB aside (mv x-ui.db x-ui.db.bak) before restore.","Prefer restoring to a fresh path and repointing the panel, so rollback stays possible."],"tags":["database","sqlite","restore","filesystem"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}