{"record":{"id":"83d9642890a7fdd0","repo":"MHSanaei/3x-ui","slug":"sqlite-backup-destination-already-exists-s","errorCode":null,"errorMessage":"sqlite backup destination already exists: %s","messagePattern":"sqlite backup destination already exists: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/database/db.go","lineNumber":2188,"sourceCode":"func IsSQLiteDB(file io.ReaderAt) (bool, error) {\n\tsignature := []byte(\"SQLite format 3\\x00\")\n\tbuf := make([]byte, len(signature))\n\t_, err := file.ReadAt(buf, 0)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn bytes.Equal(buf, signature), nil\n}\n\nfunc BackupSQLite(dstPath string) (err error) {\n\tif IsPostgres() {\n\t\treturn errors.New(\"sqlite backup is unavailable for PostgreSQL\")\n\t}\n\tif db == nil {\n\t\treturn errors.New(\"database is not initialized\")\n\t}\n\tif _, err := os.Lstat(dstPath); err == nil {\n\t\treturn fmt.Errorf(\"sqlite backup destination already exists: %s\", dstPath)\n\t} else if !errors.Is(err, os.ErrNotExist) {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = os.Remove(dstPath)\n\t\t}\n\t}()\n\n\tctx, cancel := context.WithTimeout(context.Background(), backupSQLiteTimeout)\n\tdefer cancel()\n\n\tsourceDB, err := db.DB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tsourceConn, err := sourceDB.Conn(ctx)\n\tif err != nil {","sourceCodeStart":2170,"sourceCodeEnd":2206,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/db.go#L2170-L2206","documentation":"BackupSQLite performs an online backup to dstPath and refuses to run when the destination path already exists (Lstat succeeds), to prevent silently overwriting a previous backup. It also cleans up the partial file if the backup itself fails. Any existing filesystem entry at dstPath — even a stale zero-byte file — triggers this.","triggerScenarios":"Running the backup API/CLI twice with the same destination path; a prior failed run left a file (only on non-error paths); an automated nightly backup writing to a fixed filename without rotation.","commonSituations":"Cron/scripts using backup-$(date)? No — hardcoding /etc/x-ui/backup.db; backup jobs that don't timestamp; a leftover file from a killed run.","solutions":["Use a unique destination per run, e.g. append a timestamp: backup-$(date +%Y%m%d-%H%M%S).db.","If the existing file is a stale/unwanted artifact, delete or move it explicitly before re-running.","Never point dstPath at the live database path (/etc/x-ui/x-ui.db)."],"exampleFix":"# before\ncp_cmd: BackupSQLite(\"/etc/x-ui/backup.db\")\n# after\nBackupSQLite(fmt.Sprintf(\"/etc/x-ui/backup-%s.db\", time.Now().Format(\"20060102-150405\")))","handlingStrategy":"validation","validationCode":"if _, err := os.Lstat(dst); err == nil {\n    return fmt.Errorf(\"refusing to overwrite existing backup %s\", dst)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always timestamp backup destinations: backup-20060102-150405.db.","Automate cleanup of old backups separately from creation; never reuse a fixed filename."],"tags":["database","sqlite","backup","filesystem"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}