{"record":{"id":"6b27a6b8100958a9","repo":"MHSanaei/3x-ui","slug":"restore-failed-w","errorCode":null,"errorMessage":"restore failed: %w","messagePattern":"restore failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/database/dump_sqlite.go","lineNumber":134,"sourceCode":"\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()\n}\n\n// dumpTableData appends one INSERT statement per row of table to b.\nfunc dumpTableData(db *sql.DB, table string, b *strings.Builder) error {\n\trows, err := db.QueryContext(context.Background(), `SELECT * FROM \"`+table+`\"`)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rows.Close()\n\n\tcols, err := rows.Columns()\n\tif err != nil {\n\t\treturn err\n\t}\n\tn := len(cols)\n\tprefix := `INSERT INTO \"` + table + `\" VALUES(`","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/database/dump_sqlite.go#L116-L152","documentation":"RestoreSQLite executed the dump's SQL via one multi-statement ExecContext and the driver returned an error — the dump did not apply. The function closes the connection and removes the half-built dstPath, so no partial database survives. Root causes live in the wrapped error: malformed/truncated dump, SQL features the SQLite build rejects, or destination filesystem problems.","triggerScenarios":"Dump file truncated during transfer (checksum mismatch not caught earlier); hand-edited dump with a syntax error; dump produced by a newer SQLite with features the bundled CGo SQLite lacks; disk full while replaying INSERTs.","commonSituations":"Moving dumps between hosts via copy/paste or proxies that mangle bytes; restoring dumps edited in tools that re-encode quotes/encoding; containers with a full overlay FS.","solutions":["Read the wrapped driver error — 'near ... syntax error' means dump corruption, 'disk I/O error'/'database or disk is full' means storage.","Re-generate the dump and transfer it in binary-safe mode (scp, not clipboard); compare sizes/checksums.","If the dump came from `sqlite3 .dump` of a much newer SQLite, re-dump with plain features or update the panel so its bundled SQLite matches."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Sanity-check the dump before restoring:\nhead, err := os.ReadFile(dumpPath)\nif err != nil { return err }\nif !bytes.HasPrefix(head, []byte(\"-- SQLite dump\")) && !bytes.Contains(head, []byte(\"BEGIN TRANSACTION;\")) {\n    return errors.New(\"file does not look like a sqlite dump\")\n}","typeGuard":null,"tryCatchPattern":"if err := database.RestoreSQLite(dumpPath, dstPath); err != nil {\n    if strings.Contains(err.Error(), \"restore failed\") {\n        // dstPath was removed by the function; fix the dump then retry\n        return diagnoseDump(dumpPath)\n    }\n    return err\n}","preventionTips":["Transfer dumps binary-safe (scp/rsync) and compare checksums before restoring.","Prefer dumps produced by DumpSQLite so SQLite versions match.","Restore into a scratch path first, integrity_check it, then swap in."],"tags":["database","sqlite","restore","corruption"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}